Ted 谈 ExtJS 4.1 入门
Ted 谈 ExtJS 4.1 入门
First Thoughts Learning Ext JS 4.1
January 17, 2012 | Ted Patrick
http://www.sencha.com/blog/first-thoughts-on-ext-js/
翻译: Ext 中文网 ajaxjs.com / sp42 转载请保留本页信息
作为学习 Sencha 的新手,我希望把我学习 ExtJS 4.1的一点一滴过程分享出来。其实在我个人开发人员的职业生涯中,曾接触过不少的开发环境以及应用程序的各种框架与类库,于是我就会做一个横向的比较,拿过去的经验和现在面对着的作比较。总体来说,现在 Sencha 的框架都提供了比较完善的基础平台,让构建复杂的应用程序都比较直接方便,——这点使我觉得十分不错。
解释语言 Interpreted
必须提出,对于我来说,最不容易感觉习惯的就是,包括 ExtJS 框架在内的原生 JavaScript,都是解释性的语言。尽管我表示说,我不是对解释语言很陌生,但我初衷是,存在一个编译器,它能够提示“错误 errors /警告 warnings/标志 flags、语法自动完成、成员变量的强类型、返回值与参数”等等的信息。受到 Java 所带来的影响,边输入边出现自动完成或者提示错误,都是很有效率以及生产力的做法,而这些优点往往只是属于强类型的、编译型的语言而所有!工作中缺乏这些便利的环境与设施让 JavaScript
在 Web 技术发展中显得落伍。不过,随着我慢慢了解到解释语言的本质,我意识到我已经开始享受并沉醉其中:
JavaScript 的灵活性是一把双刃剑,语言的自由性很美好却充满着意想不到的陷阱,须以最佳的实践规避之;
现代浏览器已经提供相当不错的运行时提示。好比说 Chrome 的 Console 和 JavaScript调试器,简直没有它们了不行;
ExtJS 把 JavaScript 看作一门“元语言(metalanguage)”,目的就是为“OOP/MVC/Class/加载/多态”提供支持,这对于开发大型应用程序十分有益。而且这项特性在别的框架中不是很多见。允许扩展组件和采用 MVC 的模式将是对程序员来说十分友好的特性。
采用 JavaScript 对象 和 JSON 的数据结构作为数据或组件的实例模型真是非常有效率。也就是说可以远程加载组件,变得十分简单了,即使是一个复杂的组件,将 xtype 赋予到那个 JavaScript 对象(object literals)就可以了。我现在经常注意的一个问题就是,要把代码格式化好,以避免生怕多个逗号,尤其在处理复杂的 object litreal 中。
我选用的 IDE 是
Eclipse VJET Plugin,对于 JavaScript 和 ExtJS 开发都是一款非常顺手的 IDE。不怕价比价,就怕货比货,这款 IDE 可以说在几个开源的 IDE 中自动提示最好的一个。
Ext JS “标准库” Ext JS “StandardLibrary”
ExtJS 本身提供一系列的辅助类和方法使得构建程序简单许多。学会 Ext.Array, Ext.Date,Ext.String,Ext.Object, Ext.Loader 来处理一些数据工作真的很简单。每个应用程序使用到的这些公共库的方法,例如:
Ext.require( [ 'widget.*' , 'layout.*' , 'Ext.data.*' ] );
Ext.Array.each( [ 1, 2, 3, 4 ], function( name ) {console.log( name );});
Ext.Object.getSize( { age:23 , height:6, name:"Ted" }); //3
容器 Containers
ExtJS提供非常多的容器候选使用(Ext.window.Window、Ext.panel.Panel、Ext.container.Container、Ext.grid.Panel,等等),也允许开发者内嵌地使用视图和复杂的应用程序逻辑。ExtJS 中容器布局行为都是可编程,可以在运行时中调整你的容器为“vbox”、“hbox”或者变为其他 Ext.layout.container 类型。另外你也可以动态地调整容器 flex 的配置项为某一数值。
Xtype
Xtype 是 ExtJS 中里面一个宽松的数据格式,就是可以通过一个简单对象去表示复杂的容器层次,并加以控制。十分简单也十分实用,解决掉了许多 UI 组件的问题。我以前搞过 Flex 和 Android,都是用 XML 组件化的形式,但因为同编译器工作流耦合得很紧,实例化和布局组件也十分麻烦。而在解释性系统中,这不复存在,马上就可以动态调整的了,甚至远程加载也变得十分简单。 Xtype 其实就是一个 JavaScript 对象(Object Literal),JavaScript 引擎解析本来就是一点也不费劲。无论在移动端还是桌面端都是十分快速。虽然你也可以一般方式的实例化那对象,不过采用
xtype 的方式真是好处多多。下面是由三个 xtypes 组成的一个数组:
[ { xtype: 'container', html:'First Item', flex:1 } ,
{ xtype: 'splitter' } ,
{ xtype: 'container', html:'Second Item', flex:1,maintainFlex:true } ]把这数组分配到容器的 item配置项身上,就会渲染几块区域的视图。个人觉得还是简单和优雅,——您说呢?
样式 Styling
我曾在不同的框架中搞过组件样式的问题。ExtJS 把样式方面的问题抽象出来,完全置于 CSS 中去处理,十分强大的说。采用 SASS/Compass 源码的方式来生成 CSS 的一大好处是修改容易。在 Flex 或者 Android 中,就相应缺乏这种手灵活段,它是在编译时写死的。解释语言就是有解释语言这点的好处。简单修改 ExtJS 的基调颜色不算困难,但是深入样式的话,学习好 CSS 还有很长的路要走。
Hello Sencha
这是我在 Sencha.com 的第一篇博文,我想还是尽量要贴一贴简单的例子为好,下面这个例子是创建包含有一张图片的窗体。直接在写字板中贴上下面代码然后在浏览器打开即可。
<!DOCTYPE html>
<html>
<head>
<title>Hello Sencha</title>
<!--styles-->
<link rel="stylesheet"type="text/css"href="http://cdn.sencha.io/extjs/4.1.0.b1/resources/css/ext-all.css">
<!--extjs 4.0.2-->
<scripttype="text/javascript"src="http://cdn.sencha.io/extjs/4.1.0.b1/ext-all.js"></script>
<!--app-->
<script type="text/javascript">
//define the application
Ext.application({
launch: function() {
//create a window
var win = Ext.create( 'Ext.window.Window' , {
title: 'Hello Sencha',
height: 285,
width: 250,
layout: 'fit',
//add an image to the window by an xtype object
items: {
xtype: 'image',
src:'http://www.sencha.com/files/blog/old/blog/wp-content/uploads/2010/06/sencha-logo.png'
},
listeners:{
beforehide: function( com, owner, opt ){
alert('hiding win');
}
}
});
//show the window
win.show();
}
});
</script>
</head>
<body></body>
</html>我会继续记录学习 ExtJS 和 Touch 的旅程。还要学习很多的东西,不一而足,但是如果可以分享到给大家,那么真是最好不过的了。
文章
Web App开发 · JavaScript · 前端开发 · 开发工具 · 容器 · IDE · Android开发 · 数据格式 · 编译器 · 程序员
2012-02-17
你所不知道的21个云服务器应用实践———云计算综合入门指南
摘要:如今,基于互联网的服务和交互已经越来越深入我们的生活,“云计算”也成为了不折不扣的高频词汇。并以其快捷、按需调用网络资源的优势为我们的日常生活带来了诸多便利和改变。之前,我们基于云服务器的多种利用方法制作了“云计算的1024种玩法”的零基础入门专题,现在,我们对这一专题进行扩展和整合,制作出了这份云计算综合入门指南。
它分为两个部分,分别是云计算基础和云计算实践。在基础篇中,我们能学习到关于云服务器远程控制搭建,数据管理使用,以及建站环境扩展等工具性内容;而在实践篇中,我们则能看到论坛、博客、网盘的搭建以及离线下载、贴吧签到、公众号平台管理等各种各样的有趣且实用的实践。
我们相信,无论你是初次接触还是已有基础,在云计算的领域里,你都能够成为一名优秀的行家。
专题链接:https://m.aliyun.com/markets/aliyun/yq/topic/1024
云计算基础
弹性计算和轻量应用服务器的远程控制入门
随着计算机的不断普及,“云计算”三个字也越来越成为我们随时能接触到的高频词汇,但是云计算到底怎么用呢?接下来,我们将以云计算中的IaaS(基础设施即服务)为例,来介绍操作最复杂的云计算产品应该如何使用。
IaaS的代表就是弹性计算服务ECS,这项服务在云计算的应用上主要以Linux发行版为主,因此常提到的IaaS的控制主要就是基于Linux发行版(以下简称Linux)的远程控制。
Linux的远程控制主要有三大点组成:终端控制、文件传输和内容编辑。终端控制主要就是使用一些Linux操作命令,文件传输则是本地文件和服务器文件的交互,内容编辑就是对一些配置文件的编辑,主要就是用编辑器,这个很重要,很多新手使用记事本或写字板来编辑配置文件,很容易出现配置错误。
技术简谈:
首先,我们要注册一个云服务器,我们就可以去账号管理控制台查看服务器和相关配置了。关于终端控制软件,我们推荐免费开源Putty,它是一款支持Telnet、SSH、rlogin、纯TCP以及串行接口的连接软件。无需安装,解压后即可实行远程控制。关于文件传输,我们可以依靠FileZilla这款软件,这是一个快速可靠的、跨平台的FTP,FTPS和SFTP客户端,具有图形用户界面和很多有用的特性。关于内容编辑,这里推荐一下微软家的Visual Studio Code——一个轻量的跨平台代码编译器。掌握这些软件是我们利用云计算的基础,小伙伴们可一定要记好啦。
详情链接:https://yq.aliyun.com/articles/228369
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
利用安全组进行访问控制授权
安全组是一种虚拟防火墙,用于设置单台或多台云服务器的网络访问控制,它是重要的网络安全隔离手段,用于在云端划分安全域。每个实例至少属于一个安全组,在创建的时候就需要指定。同一安全组内的实例之间网络互通,不同安全组的实例之间默认内网不通,可以授权两个安全组之间互访。
在进行云服务器开发时,很多新朋友都有这样的经历:明明我已经搭建好了Web服务,为什么还是无法访问呢,随后就不停对 Web 服务软件像 Nginx 进行排错,然后再一个劲的找 iptable 、Firewall 或者 ufw 是否出现了问题。而如果在一开始的时候设置好安全组,就能够轻松的避免这些麻烦。
技术简谈:
在购买云服务器时,我们能够在网络设置处看到关于安全组的选项,用于调整需要开通的端口,通常情况下,80、443、22和3306基本上能够通吃大部分的弹性计算端口应用场景,而开通后进一步设置还可以满足更进阶的需求。
详情链接:https://yq.aliyun.com/articles/228290
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
使用DMS只要一个浏览器轻松搞定运维任务
新手在学习云计算的时候,特别是IaaS类产品远程操作起码得下个三大件,SSH工具、SFTP工具和代码编辑器,一下就是三个软件而且还很有可能下成英文版,很多小伙伴或许到这一步就开始“我是谁,我在哪儿,我要干什么”了。不过不要怕,这里推荐一款深藏功与名的强大产品——DMS(数据管理)
谈到DMS的云服务器控制台,它起初的名字叫CDE,后来,经过同RDS控制台的合并,才形成了如今我们看到的数据管理DMS。它能够通过可视化的界面帮你轻松完成服务器操作的三大块(终端管理、文件传输和内容编辑),尤其对于第一次触碰 Linux 服务器的小伙伴,这绝对是你上手Linux的利器!
技术简谈:
登录DMS相关网页,选择一个云服务实例,就能够进行终端控制界面。关于文件管理的部分,只要在菜单处点击文件管理,就可以直接方便的进行文件上传。双击文件即可唤出代码编辑器,自动高亮、正则替换等功能,完全可以满足应急使用的需要。同时,防火墙,计划任务能拓展功能也在DMS上实现了图形化,十分直观高效。
详情链接:https://yq.aliyun.com/articles/228245
服务购买:https://www.aliyun.com/product/dms
安装 MariaDB 并通过DMS管理
MariaDB数据库管理系统是 MySQL 的一个分支,主要由开源社区在维护,采用GPL授权许可。MariaDB的目的是完全兼容 MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。而DMS作为可视化的数据管理平台,可以方便的同云服务器进行链接,从而形成了一种简单直接就能对MariaDB进行管理的方法。
技术简谈:
利用Ubuntu安装MariaDB Server并进行对应的安全设置。开放防火墙端口用于账户的远程连接,修改数据库使其能线上访问,添加云服务实例安全组的规则,并选择MySQL协议,使端口仅向DMS的IP开放,在DMS控制台中连接数据库,最终对MariaDB进行清晰直观的管理。
详情链接:https://yq.aliyun.com/articles/228377
服务购买:https://www.aliyun.com/product/dms
手把手教你如何编译与升级一个高性能OpenResty
OpenResty是一个基于Nginx与Lua的高性能Web平台,并且拥有非常优秀的拓展性,手动编译OpenResty也能够使得服务器能够发挥出更加优秀的性能。于此同时,我们还可以通过将全部依赖限制在Linux发行版组建内的方法,来免除编译带来的后期维护成本。
技术简谈:
设置好远程控制和安全组相关,在SSH终端中输入代码设定版本变量,而后安装依赖组件,下载OpenResty源码并放置在/root/src目录下以方便管理。最后,对OpenResty进行编译,设置服务和开机启动,打开你的服务器IP,就可以看到安装好的OpenResty提示页了。如果需要对OpenResty的版本进行升级,则只需要重新定义版本和下载源码,直接进行升级即可。
详情链接:https://yq.aliyun.com/articles/228399
https://yq.aliyun.com/articles/257098
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
手把手学会配置安装LNMP建站环境
在云计算普及之前的VPS时代,网络环境几乎都是靠LNMP(Linux + Nginx + MySQL + PHP)支撑起来的,它的优点在于配置方便,且相较于LAMP(Linux + Apache Httpd + MySQL + PHP)在低配硬件上表现更为优异。但实际上,LNMP的创新可远不止如此。
我们可以配置一个性能优异、灵活自由而且升级方便的进阶型LNMP建站环境。它由Ubuntu + OpenResty + MariaDB + PHP组成,兼容LNMP的习惯但是更加灵活和可靠。
技术简谈:
首先,设置好开放80和443端口的安全组,避免难以分析访问失败还是编译失败的问题,然后安装OpenResty与PHP,并对PHP进行设置,创建虚拟子主机使OpenResty 、MariaDB 和PHP加以结合,从而令OpenResty能够进行PHP-FPM的加载。
详情链接:https://yq.aliyun.com/articles/257093
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
使用 NAS 文件储存高性价比提升磁盘性能
文件存储服务(Network Attached Storage,简称NAS)是面向云服务实例、HPC和 Docker等计算节点的文件存储服务,它提供标准的文件访问协议,用户无需对现有应用做任何修改,就能够使用具备无限容量及性能扩展、单一命名空间、多共享、高可靠和高可用等特性的分布式文件系统。
这是一个常见的问题:我们已经有了优质的云服务器,但默认的磁盘容量可能依旧有些难以满足需求,这时候,NAS就成了绝不能错过的项目,更低的价格、更高的性能、以及容量增量计算,用多少算多少的的算法让NAS成为了“薅羊毛”般优惠扩充磁盘性能的不二之选。
技术简谈:
首先进入NAS控制台,点击创建文件系统以购买与ECS对应地域的NAS。Linux选择NFS即可。安装nfs协议软件RHEL/CentOS,利用NFS4协议进行挂载,运行df-h查看挂载情况,末行即可显示目前挂载好的NAS目录
详情链接:https://yq.aliyun.com/articles/257099
服务购买:https://nas.console.aliyun.com/#/ofs/list
云计算实践
为求职加分:为自己建个炫酷的简历网页
对广大学生群体来说,个人简历堪称通向未来工作岗位的第一把钥匙,一份优秀而又美观新颖的简历不仅会让你与其他人与众不同,做到脱颖而出,甚至还可以让面试官因为你的独特而留下极佳的印象。
比起屡见不鲜的office简历,一份网页形式的简历会显得独特而出彩很多,那么,该怎样利用服务器和云计算创造出独属自己的炫酷简历呢?一起来看吧。
技术简谈:
开通自己的云服务器,在免费镜像市场下配置LAMP环境(Ubuntu16.04 Apache PHP7.1),通过Visual Studio
Code(简称VSC)
编辑并制作简历页面。并利用简单的代码实现简历的编辑,排版以及美化,最后使用FileZilla将制作好的简历上传到服务器端。
详情链接:https://bbs.aliyun.com/read/534380.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
搭建个人博客
对于这项内容,有的朋友可能会发出这样的疑问:不就是一个博客吗,有什么新鲜的?注册一个不就好了嘛?
先别急,在这篇文章里,你能够学到的是一个独属于自己的,不会冠以任何非个人title且拥有更多freestyle界面的博客。创建独一份的博客平台,编写丰富而精彩的博客内容,连后台维护也丝毫不用担心,有点小心动的话,就来动手做一个吧。
技术简谈:
开通自己的云服务器,用自己喜欢的方法搭建镜像LAMP环境(Ubuntu16.04 Apache PHP7.1),通过FileZiila自虚拟机上上传文件,利用PuTTY进行 xterm 终端的模拟,最后下载WordPress,将搭建好的博客上传到服务器端。
详情链接:https://bbs.aliyun.com/read/534383.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
拥有一个自己的论坛
大家或许都遇到过这样的小麻烦:当志同道合的人越来越多时,传统的群聊不仅很容易错过内容,造成信息流失,时不时的跳出的消息也会令人感到心情烦躁。
那就建立一个论坛好了。它堪称一个接近完美的工具,定期的浏览不会浪费时间,分门别类的整理也使重要内容的推送变得明确,何况还有高自由度的网盘可用,这可比传输速度感人的免费网盘和群文件方便迅捷的多。
接下来,我们就来学习一下怎样搭建一个便于沟通和交流 phpwind 论坛。phpwind(简称:PW)是一个基于PHP和MySQL的开源社区程序,是国内最受欢迎的通用型论坛程序之一。值得一提的是,阿里云的开发者论坛就运行在 PW 之上,非常的稳定和高效。
技术简谈:
开通一个云服务器,安装具备PHP 和 MYSQL 的LAMP环境(推荐Linux + Apache + MySQL + PHP),安装PuTTY同开通好的云服务器进行链接,安装phpwind,并使用FileZilla上传到服务器上的/data/wwwroot/default/目录下。最后整理论坛的板块和名称,确定核心内容,开始论坛的经营之旅。
详情链接:https://bbs.aliyun.com/read/534399.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
用弹性计算搭建个人云笔记服务
如今互联网的发展可谓一日千里,新概念也层出不穷,对紧跟前沿的IT工作者们来说,一份易于备份的云笔记可谓是辅助学习的最佳帮手。
在常见的云笔记平台中,大量图片和视频的引用往往不被允许或价格高昂,针对这一难题,阿里云特别推出了全民云计算和大学生的云翼计划等活动,可以很方便的解决这个问题。
———我们可以自建一个云笔记服务,并进行朋友或团队间的共用和共享,不仅价格便宜,自定义性也强得多,如果将我们的笔记当作博客公开出来,再绑定一个域名,那这就是一个个人或团队的博客了。
技术简谈:
选择Leanote作为我们自建的开源云笔记程序(Leanote是一款开源云笔记软件,使用Go的Web框架revel和MongoDB开发完成)。在 SSH 中运行以安装 MongoDB,下载Lenote二进制包并进行解压和数据库的导入,进入弹性计算服务公网IP,并使用Leanote进行笔记的创建,修改和后台管理任务。
详情链接:https://bbs.aliyun.com/read/534398.html
服务平台:https://leanote.com
打造个人专属网盘
对学生来说,查阅文献可谓是学习和知识积累的必由之路,但论文一多,整理就成了难题。
无论是论文的保存还是分门别类的整合,一份个人网盘都能很方便的将其解决,让我们做到在线登录,在线阅读,在线管理,满足学习和工作的需要。
那就在自己的电脑上搭一个好了。然而,在个人的电脑上的网盘搭建不仅需要极高的硬件配置,有时还面临着随时崩溃的风险,这些潜在的风险处理起来还是比较麻烦的。
那就把这个任务交给云计算吧,只需要网页就可以登录个人网盘并且轻松进行管理,ECS网盘不仅建立起来简单方便,玩起来更方便,而且在之后的日子里还可以重装镜像用于其它功能,可谓稳赚不赔。
技术简谈:
开通自己的云服务器,安装Nextcloud网盘必要的Linux环境(推荐使用 LAMP环境镜像(Ubuntu16.04 Apache PHP7.1)),安装PuTTY的软件进行和云服务器的远程连接,安装FileZilla软件,获取远程 Linux 上的目录,并下载Nextcloud文件从而上传到服务器目录下。打开公网IP,创建网盘管理员账号,并安装网盘客户端进行云同步和文件读写。
详情链接:https://bbs.aliyun.com/read/534388.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
轻松打造离线下载服务器
小伙伴们有没有享受过离线下载的便利呢?然而,随着如今迅雷远程下载的关闭,旋风下载停服,网盘的花式限速,在下载一些冷门资源的时候,几kb甚至几B的情况想必也没少让我们绝望。别担心,云计算可以帮你解决这个问题。
实际上,离线下载主要分为两个部分,一是在服务器上实现的离线下载,二则是对服务器上文件的读取。例如,我们下载一个国外的文件资源,直接下载的速度无限接近于零,那这时候我们把下载迁移到位于海外的云服务器上,然后再对服务器上处理好的资源进行读取,速度就会数倍乃至数十倍的增加。在选择中转服务器时,国内外连接速度和带宽资费都是要考虑的内容。
技术简谈:
购买自己的云服务器,安装相关Docker加速脚本,使用容器化的Aria2作为下载工具(可以下载HTTP、FTP、磁力、BT等),利用 AriaNG 实现网页版的图形化控制,达成网页版迅雷一般的效果。最终通过
FileRun 实现文件的下载、预览和共享,让我们的离线下载更加多用途化。
详情链接:https://bbs.aliyun.com/read/534397.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
搭建Linux学习平台
作为大名鼎鼎的Unix类操作系统,Linux平台有着数以万计的拥簇者。想要学习Linux的话,通常的选择便是在自己的电脑上搭建Linux系统,可以直接安装Linux,或者是选择装一个双系统,安装虚拟机等等。
其实,如果想避免萌新误操作带来各种各样的麻烦,我们可以选择在云服务器上搭建一个Linux学习平台,简单方便,折腾起来同样足够安心。
技术简谈:
注册自己的云服务器,安装putty远程连接和管理机器。打开putty,并输入服务器主机号、用户名、密码,完成对主机的链接。利用FileZilla来实现本地机器与云服务器上的Linux文件读写。代码编辑则可以选择Visual Studio Code,如果觉得命令行界面枯燥难看的话,利用详情中提供的官方镜像,可以让界面图形化,变得直观起来。
详情链接:https://bbs.aliyun.com/read/534402.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
为喜欢的人建一个网站
曾几何时,你是不是也为不知道怎么向心爱的人表白而苦恼,书信略显古老,微信稍微有点随意,那么,你有没有想过可以利用云计算来表白呢——“为喜欢的人建一个网站”。即使您是能够“攻城掠地”的工程师,也会苦于没有后台服务器的支撑,没关系,这里就让云计算为您解忧;无论你是否是IT人士,有没有网页编程基础都没关系,在云计算的加持下,您只要能够把爱表达出来就足够了。
在云服务器的支撑下,搭建一个属于自己的网站。你可以搭建一个网页平台,并用HTML5编出自己喜欢的动态网页内容。
技术简谈:
开通一个云服务器,并搭建镜像LAMP环境(Ubuntu16.04 Apache PHP7.1),安装Visual Studio
Code(VSC)和FileZilla来实现HTML5代码的编写和上传,利用提供的代码技巧,把想说的话统统用动态的图文表述出来。
详情链接:https://bbs.aliyun.com/read/534389.html
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
为小伙伴搭建一个功能丰富的百度贴吧云签到
相信很多朋友都对贴吧感兴趣吧,作为一个堪称广阔的中文社区,众多的贴吧用户在吧务的管理下源源不断的为相关领域提供着非常优秀的贴子,其中不乏专业和深刻的分享。
然而随着贴吧等级的推出,签到就成了麻烦事,低等级的贴吧只有点来点去才能解决,至于一键签到,更是会员们的专利,万一哪天忘了,还得购买补签卡才能留住记录。这时候,传说中的贴吧云签到就非常的实用了。
利用云服务器搭建一个云签到功能,不仅成本低廉,而且还能够通过插件的安装来实现云灌水、云点赞、邮件提醒等待甚至面向吧务的云管理,可谓相当实用啦!
技术简谈:
注册一个云服务器并配置LAMP环境(依旧推荐Linux + Apache + MySQL + PHP),来安装OneinStack镜像。用PuTTY进行面向服务器的连接和管理,下载贴吧云签到的源文件,利用FileZilla上传到服务器/data/wwwroot/default/目录下,从而添加一个新的数据库。设计好定时任务后就可以开始每天的自动签到功能了,如果想对功能进行扩展,还有丰富的插件库可以进行选择。
详情链接:https://yq.aliyun.com/articles/186234
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
使用弹性云计算+微擎打造微信公众号管理系统
你有没有在微信公众号上见过这些通道呢?“听五首歌判断你的女子力”、“回答问题计算九型人格”。这些有趣的功能并不由微信官方提供,事实上,他们都要通过运营者自己的服务器才能加以实现。如果你运行着一个微信公众号,并想加入一些独特而有趣的小功能,提供更多玩法的话,利用自己的云服务器进行公众号管理就成了一项合适的选择。
技术简谈:
还是不变的云服务器环境注册和LAMP镜像配置,以及PuTTY和FileZilla的使用。随后,我们需要进行“微擎”这一公众号自助引擎的安装和注册。在微擎上绑定微信公众号,建立新内容并添加开发者信息,接下来,我们就可以添加自己想完成的公众号应用,从而进行内容的设置,使用户能够直接扫二维码进行使用了。
详情链接:https://yq.aliyun.com/articles/203457
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
云端打造家庭文件备份中心
看到这里,想必大家都对云服务器用途的广泛性有了充分的认识,接下来,我们介绍的就是利用它来打造一个家庭文件备份中心。
我们使用的软件是Syncthing,其所有的信息通讯都会用TLS进行加密,Syncthing文件同步不同于网盘需要经过中转服务器,进一步增强了安全性。Syncthing 还有多项强大的版本控制功能:分别是回收站式版本控制、简易版本控制、阶段版本控制、以及外部版本控制。
技术简谈:
开通一个云服务器,选择Ubuntu 16.04 64位系统,进入实例安全组并添加一个规则,默认开放所有的端口并允许访问。利用PuTTY进行终端的登录,利用FileZilla进行上传。安装和配置Syncthing并同样在本地电脑上安装,接下来,只要添加特定的远程设备,就可以完成家庭文件的备份和调用了。以及,Syncthing支持 Windows、macOS、iOS、Android 等多种系统,我们完全可以利用全面云计算的弹性计算服务,来实现一个自用NAS轻松对家庭文件进行同步。
详情链接:https://yq.aliyun.com/articles/203352
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
搭建 wiki 知识库
Wiki的含义是百科。它是知识管理系统中一个非常好用的形式,但对于以维基为代表的网络百科而言,不仅内容门槛较高,自有管理也会在很大程度上受限。
如果你运营着一个公司部门,学生组织,或者兴趣社团的话,完全可以通过自建一个 Wiki 知识库来管理部门,社团组织或学生会里的一些小常识、小知识,从而极大的优化对于整个群体的管理和安排。
技术简谈:
注册云服务器并配置LAMP环境来安装OneinStack镜像。用PuTTY进行面向服务器的连接和管理,利用FileZilla进行内容上传。接下来,我们需要一份MediaWiki的源文件,将它上传到注册好的云服务器中,并为这一目录添加权限。选择SQLite数据库,输入wiki名称和管理员账户并上传所需文件后,就可以进行百科的新建、编辑和使用了。
详情链接:https://yq.aliyun.com/articles/203078
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
打造自己的在线编程环境
大家有没有这样的经历呢?在学校上着编程实验课,不小心遭遇断电或者按到重启,做了大半节课的内容还没保存就被还原卡干净的直接归零,或者配置差的电脑直接给你摆了个大花脸——蓝屏,这就很气了。同时,机房和打印社还容易让U盘感染病毒:把所有文件锁的干干净净,说起来,五月份的永恒之蓝还历历在目呢。
这时候,如果利用云计算打造一个在线编程环境,只通过浏览器就可以进行代码的编辑和上传了,既方便还不易丢失。前面的文段中,我们有提到如何搭建一个基于云服务的Linux学习平台,现在,我们就进行一下扩展,打造自己的在线编程环境。
技术简谈:
云服务器的使用方法暂且不再赘述,为了实现编程环境的在线化,我们需要一个容器化的 WebIDE。打开弹性计算服务的公网IP,我们就能看到工作窗口。我们登陆查看一下IDE的公钥,复制其内容,粘贴到 Github 对应库中的 Setting—Deploy Keys—Add deploy key,然后复制我们的库的git地址,并粘贴到 WebIDE,点击Create,等待生成,打开出现的库,就能在浏览器中看到熟悉的IDE编程界面了。写完代码点击推送,就可以在Github看到更新好的内容了。
详情链接:https://yq.aliyun.com/articles/212990
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
部署 Ghost 博客
Ghost是一个开源而轻量的博客平台,NodeJS语言的应用不仅让它拥有了非常优秀的访问速度,同样也减缓了服务器的压力。
前阵子,这个博客平台推送了1.0版本的更新,为我们带来了全新的编辑器、全新的后台UI、新的默认主题以及夜晚模式等等。同时,Ghost 1.0还配套搭载了Ghost-cl,这是一个非常强大和方便的工具,可以更方便的通过命令行设置 Nginx、Mysql、SQlite3或者开机启动等等。
不过,Ghost 1.0 的安装方式也带来了比较大的更新,和过往版本也不尽相同。
技术简谈:
安装和配置好云服务器后,我们需要更新系统并安装器所需要的语言依赖:例如Node.js V6 LTS,Yarn等等。为了避免后面使用 ghost 的权限问题以及安全设置,这里还需要创建一个 ghost 账户。安装完成后,就能够进入后台完成博客的编写啦。
详情链接:https://yq.aliyun.com/articles/220115
服务购买:https://promotion.aliyun.com/ntms/campus2017.html
文章
Web App开发 · 关系型数据库 · Linux · 云计算 · 数据库管理
2017-11-29
基于 SurfaceView 详解 android 幸运大转盘,附带实例app
基于 SurfaceView 详解 android 幸运大转盘,附带实例app
首先说一下,幸运大转盘,以及SurfaceView是在看了也为大神的博客,才有了比较深刻的理解,当然这里附上这位大神的博客地址:博客地址,有兴趣的话你可以去看看,里面有很多的例子。至于我为什么要写这篇博客?,原因之一:加强自己的理解,原因之二:大神的博客就是大神的博客,跳转的太快,基础不好的,很难理解。还有就是一天在实验室太无聊了,没事写写东西。这里我再来更加基础的分析一下。写的不好,原谅。有什么写的不对的地方还望指出,谢谢。附上QQ,863744353.有意者交流交流。接下来切入正题,我们来详细解说一下这个大转盘。
1.首先附上效果图以及简单的分析
这个效果图,是不是很眼熟,当然这个就是最后的效果图,货真价实的,一点不偏差,还比这个流畅。因为这个实在虚拟机上面截取的,你也知道虚拟机的流畅度和真机是没有办法比的。
1.首先分析一下,做这个大转盘,都需要实现什么。
不难看出做这个大转盘需要两个控件,一个是自定义的SurfaceView,还有一个当然就是中间的永远不动的按钮指针,每次点击只是换一下图片就行了。
2.说一下实现这个的基本思路。
实现这个看起来是不是很难,当然对于一些大神级别的人物来说,这个就是小菜一碟,但是关键是大多数还不是。咋眼一看,不就是一个盘子在不停的转么。中间多了一个控制盘子的按钮。要实现盘子不停的旋转就得靠这个SurfaceView了,查阅官方ApiSurfaceView直接直接父类是View,SurfaceView与其他的View有一个重要的却别,那就是SurfaceView允许非UI线程修改,这个也上市SurfaceView的一大优点吧,
这样就不用用一个View每次还得通过非UI线程去通知UI线程修改视图,多麻烦啊。 所以通过,SurfaceView你就可以开一个线程在后台不断的更新UI,方便多了。、
2.SurfaceView的常用模板
虽然SurfaceView很方便做一个小游戏,但是也不随便就乱来,网上有个比较常用的模板,相信这样做既能打到你的目的又能事半功倍,何乐而不为啊。
public class SurfaceViewTemplate extends SurfaceView implements Callback,Runnable{
//定义一个SurfaceHolder 用于接受获取的SurfaceHolder
private SurfaceHolder mHolder;
//用于获取绑定mHOlder的Canvas
private Canvas mCanvas;
//用于不断绘图的线程
private Thread mThread;
//用于控制线程的开关
private boolean isRunning;
//这个时代三个参数的构造函数,一般自会在有使用自定义属性的时候才会调用这个构造函数
/**
* @param context 上下文
* @param attrs xml文件定义的属性
* @param defStyle 自定义属性
*/
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
//当时用xml文件定义这个自定义View的时候,就会调用这个带两个参数的构造函数
public SurfaceViewTemplate(Context context, AttributeSet attrs) {
super(context, attrs);
//获取SurfaceHolder
mHolder=getHolder();
//添加callback
mHolder.addCallback(this);
//设置一些属性,焦点,屏幕常亮
setFocusable(true);
setFocusableInTouchMode(true);
setKeepScreenOn(true);
}
//当是在代码里面显示的定义的时候就会调用这个带一个参数的构造函数
public SurfaceViewTemplate(Context context) {
//在这里我们让他去调用带两个参数的构造函数,以便就算是在代码里面定义的也能完成一些初始化操作
this(context,null);
}
//主要,也是最核心的工作都是在run方法里面执行的,如draw()
@Override
public void run() {
try{
//这里通过死循环,不断的进行绘图,给你一种盘在不断旋转的错觉
while (isRunning){
draw();
}
}catch(Exception e){
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
//在这里做一些初始化的工作,开启线程。。。
@Override
public void surfaceCreated(SurfaceHolder arg0) {
//实例化线程,并设置isRunning
isRunning=true;
mThread=new Thread(this);
mThread.start();
}
//当SurfaceView执行destroy的时候关闭线程
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
//关闭线程只需设置isRunning
isRunning=false;
}
//这里制定以下控件的宽高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
3.接下来就根据大转盘,定义成员变量
不得不说,成员变量确实有点多,出去模板给的几个外,还得定义,文本,图片,图片地址,各种。。。有点记不住,附上代码
// surface
private SurfaceHolder mHolder;
// 与surface绑定在一起的Canvas
private Canvas mCanvas;
// 用于绘制的线程
private Thread mThread;
// 线程的控制开关
private boolean isRunning;
// 描述抽奖的文字
private String[] mName = new String[] { "单反相机", "IPAD", "手气不好", "IPHONE",
"张杰一枚", "手气不好" };
// 每块的颜色
private int deepColor = 0xFFFFC300;
private int lightColor = 0xFFF17E01;
private int[] mColors = new int[] { deepColor, lightColor, deepColor,
lightColor, deepColor, lightColor, };
// 与文字对应的图片
private int[] mImgs = new int[] { R.drawable.danfan, R.drawable.ipad,
R.drawable.f040, R.drawable.iphone, R.drawable.meizi,
R.drawable.f040 };
// 与文字对应的图片的数组
private Bitmap[] mImgsBitmap;
// 盘块的个数
private final int mItemCount = 6;
// 绘制盘块的范围
private RectF mRange = new RectF();
// 圆的直径
private int mRadius;
// 绘制盘块的画笔
private Paint mArcPaint;
// 绘制文字的画笔
private Paint mTextPaint;
// 滚动的速度
private double mSpeed;
private volatile float mStartAngle = 0;
// 递减的加速度
private int aSpeed = 1;
// 是否点击了停止
private boolean isShouldEnd;
// 控件的中心位置
private int mCenter;
// 控件的padding
private int mPadding;
// 背景图片
private Bitmap mBgBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.bg2);
// 文字的大小
private float mTextSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics());
public LuckyPadView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
这些基本上都是不可少的,当然你可以简化一些,例如,减速的家速度,以及你已经显示的知道了盘的块数就是6个,每次用的时候写六个就行。
4.编写构造方法
这个构造方法嘛,有多重编写方式,看自己的用途如果只是在xml文件里面定义,且不带自定义属性的时候,就还可以忽略一个和三个参数的构造函数直接编写两个参数的构造函数,当然为了兼容性,建议三个构造函数都支持,以免不必要的麻烦嘛,照例附上代码:
//做一些初始化的操作
public LuckyPadView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 获得holder,和与之关联的Canvans
mHolder = getHolder();
mHolder.addCallback(this);
// 设置可获得焦点,以及常亮
setFocusable(true);
setFocusableInTouchMode(true);
setKeepScreenOn(true);
}
//去调用三个参数的构造函数
public LuckyPadView(Context context, AttributeSet attrs) {
this(context,null,0);
}
// 一个参数的构造函数去调用两个构造参数的构造函数
public LuckyPadView(Context context) {
this(context, null);
}
5.编写onMeasure 方法
写自定义控件的时候,不能忽略的一个方法,他指定了自己的大小,以及子控件最大的大小。。。不多说,有一点值得说一下,就是控件的大小是以宽高当中最小的为为基准这样做的目的是为了让控件能成一个正方形,方便以后绘制圆形,以确定半径,中心等,所以在xml文件里面定义的时候别忘了centerInParent。废话不多说,附上源码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获得宽高当中最小的
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int min = width < height ? width : height;
// 获得圆的直径
mRadius = min - getPaddingLeft() - getPaddingRight();
// 获得padding值,一paddingleft为基准
mPadding = getPaddingLeft();
// 设置中心点
mCenter = min / 2;
setMeasuredDimension(min, min);
}
6.编写 surfaceCreated 顺便附上 surfaceDestroyed
这两个方法的作用,见名知意。在create的时候我们需要实例化线程,并且将线程开启,以及初始化一些成员变量,如:mRange确定绘图区域,附上一张图就理解了。在destroy的时候我们必须把线程关闭不然就会造成一个严重的后果,内存泄露哦。附上代码:
// 做一些初始化的工作
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// 初始化绘制圆弧的画笔,并设置锯齿之类的
mArcPaint = new Paint();
mArcPaint.setAntiAlias(true);
mArcPaint.setDither(true);
// 初始化绘制文字的画笔
mTextPaint = new Paint();
mTextPaint.setColor(0xFFffffff);
mTextPaint.setTextSize(mTextSize);
// 圆弧的绘制范围,绘制的范围刚好是一个正方形,这个我得做一个插图(1),不然理解不了
mRange = new RectF(mPadding, mPadding, mRadius + mPadding, mRadius
+ mPadding);
// 初始化图片
mImgsBitmap = new Bitmap[mItemCount];
for (int i = 0; i < mItemCount; i++) {
mImgsBitmap[i] = BitmapFactory.decodeResource(getResources(),
mImgs[i]);
}
// 开启线程
isRunning = true;
mThread = new Thread(this);
mThread.start();
}
// 主要是用来关闭线程的
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// 通知线程关闭
isRunning = false;
}
7.重头戏 run方法
基本上所有的操作都是在这个run方法里面执行的,当然了为了代码的可阅读行,我们打run方法里面的各种操作,打包成方法放在了外面。主要打包的方法draw(),绘图操作。通过一个死循环,不断的进行绘图。给用户一种转盘在不断的旋转的感觉。但是由于绘图是由计算机执行的,所以你是不知道他的执行时间是多少,而且计算机在某一时刻的性能也是不确定的,所以就会造成绘图的时间有差异,这样不就造成了假速度时快时慢这样可不行。所以有一个很巧妙的处理方法,就是记录你绘图的时间。然后你给一个绘图的标准时间值,当小于这个绘图时间的时候,就让线程休眠不足的时间,这样就很好的解决了。附上代码:
@Override
public void run() {
// 不断地进行绘图,这样就给你一个错觉,转盘在不停的转
while (isRunning) {
// 这一次开始绘图的时间
long start = System.currentTimeMillis();
// 真正的绘图操作
draw();
// 这一次绘图的结束时间
long end = System.currentTimeMillis();
// 如果你的手机太快,绘图分分钟的事情,那也得让他把那个50等完
try {
if (end - start < 50) {
Thread.sleep(50 - (end - start));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
8.run() 方法里面的 draw()
在这个draw()方法里面工作就来了,你首先需要获得与mHolder绑定的Canvas,然后绘制背景,一次绘制每个块的背景,然后再绘制块上面的文字,每个快上面的图片。。。当然每个快的位置是根据mStartAngle和速度以及sweepAngle,tmpAngle计算出来的,还得判断用户是否摁下了停止按钮,如果是,得按一定的加速度减速。当然速度不能小于零所以最后还得判断一下,以便将速度回执到0。对了知道你有没有注意到,各种try{}catch(){},这个是为了防止不知什么时候来个异常之类的,我可不希望我的转盘半路crash了,附上代码:
// 绘图
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
if (mCanvas != null) {
// 首先绘制背景图
drawBg();
// 绘制每个弧形,以及每个弧形上的文字,以及每个弧形上的图片
float tmpAngle = mStartAngle;
float sweepAngle = (float) (360 / mItemCount);
for (int i = 0; i < mItemCount; i++) {
// 这个就是传说中的,背景颜色
mArcPaint.setColor(mColors[i]);
// 这里真的画了一个扇形出来,干脆我在这里也弄一个插图算了(4)详细可以参见
// http://blog.sina.com.cn/s/blog_783ede0301012im3.html
// oval :指定圆弧的外轮廓矩形区域。
// startAngle: 圆弧起始角度,单位为度。
// sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。
// useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。
// paint: 绘制圆弧的画板属性,如颜色,是否填充等。
mCanvas.drawArc(mRange, tmpAngle, sweepAngle, true,
mArcPaint);
// 绘制文本
drawText(tmpAngle, sweepAngle, mName[i]);
// 绘制Icon
drawIcon(tmpAngle, i);
// 转换角度,不能再一个地方一直绘制,
tmpAngle += sweepAngle;
}
// 当mspeed不等于0时,相当于滚动
mStartAngle += mSpeed;
// 当点击停止时,设置mspeed慢慢递减,而不是一下就停了下来
if (isShouldEnd) {
mSpeed -= aSpeed;
}
// mspeed小于0的时候就该停止了
if (mSpeed < 0) {
mSpeed = 0;
isShouldEnd = false;
}
// 根据当前旋转的mStartAngle计算当前滚动的区域
callInExactArea(mStartAngle);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null)
mHolder.unlockCanvasAndPost(mCanvas);
}
}
这里面有一个绘制每一个块的背景,插图理解一下
mCanvas.drawArc(mRange, tmpAngle, sweepAngle, true,
mArcPaint);
9. draw()方法里面的drawBg()
首先得附上一张图片不然看不懂啊,通过这个图相信你能够非常清楚的看清楚接下来的代码了,无外乎就是在mRange的外面绘制了一圈,用于美观的背景。作用不大,不多说,附上代码:
// 绘制背景图
private void drawBg() {
// 根据当前旋转的mStartAngle计算当前滚动到的区域 绘制背景,不重要,完全为了美观
mCanvas.drawColor(0xFFFFFFFF);
// 这里这个绘图一般又看不懂了,得有个插图才行,这个貌似比圆弧的绘制范围大了那么一圈
mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding / 2,
mPadding / 2, getMeasuredWidth() - mPadding / 2,
getMeasuredWidth() - mPadding / 2), null);
}
10.draw()里的drawText()方法,用于绘制块里面的文本
首先上一张图,绘制这个文本确实有点麻烦,毕竟文本的位置有点特殊。
绘制这个文字我也是理解了很久,真是有点绕。绘制这个文字是通过path先确定一个Arc,为一个弧形,然后通过水平和垂直偏移量共同定位文字的最终位置,图中有标示。float hOffset = (float) (mRadius * Math.PI / mItemCount / 2 - textWidth / 2);解释一下这个公式首先得到弧长,然后减去文字宽度的一半就得到了水平偏移量,垂直偏移量就跟好理解了直接是float vOffset = (float)
(mRadius / 2 / 6);也就是半径的1/6 附上代码:
/**
* 绘制文本
*
* @param startAngle
* @param sweepAngle
* @param mName2
*/
private void drawText(float startAngle, float sweepAngle, String mName2) {
// path
Path path = new Path();
// 将写字区域加上去
path.addArc(mRange, startAngle, sweepAngle);
// 文字的宽度
float textWidth = mTextPaint.measureText(mName2);
// 利用水平偏移和垂直偏移让文字居中,是不是理解不了 ,我也是,画个插图,(3)
float hOffset = (float) (mRadius * Math.PI / mItemCount / 2 - textWidth / 2);
float vOffset = (float) (mRadius / 2 / 6);
// 得把文字画上去了
mCanvas.drawTextOnPath(mName2, path, hOffset, vOffset, mTextPaint);
}
11.draw()方法里面的drawIcon()
首先还是附上一张图片,便于理解,毕竟绘这个图也不是那么容易理解。 图片是不是很详细。要绘制这个图我们首先得确定一个Rect,而这个Rect就是插图阴影的部分,只要确定了这个阴影部分,绘图就很简单了。关键就是确定这个阴影部分。首先我们通过那个平分角和startAngle得到X和Y通过三角函数,别告诉我你忘了。然后通过mCenter加上和减去等等操作得到了,最后的阴影部分,最后附上代码:
/**
* 绘制Icon
*
* @param startAngle
* @param i
*/
private void drawIcon(float startAngle, int i) {
// 设置图片的宽度,为直径的1/8,当然可以随便改
int imgWidth = mRadius / 8;
// 换算成弧度
float angle = (float) ((30 + startAngle) * (Math.PI / 180));
// x,y ... 这个或许要一张图篇才能理解,(5)
int x = (int) (mCenter + mRadius / 2 / 2 * Math.cos(angle));
int y = (int) (mCenter + mRadius / 2 / 2 * Math.sin(angle));
// 确定绘制图片的位置
Rect rect = new Rect(x - imgWidth / 2, y - imgWidth / 2, x + imgWidth
/ 2, y + imgWidth / 2);
// 绘制
mCanvas.drawBitmap(mImgsBitmap[i], null, rect, null);
}
12. luckyStart(index)方法
这个方法,就是显示的在转盘点击开始后,开始转之前根据计算结果设置转盘最后的结果。是不是很想骂一些电商,就知道玩弄我们的感情。结果早就知道了。当然有点难理解,很多公式,先附上一张图:
只要将组后的结果控制在这个210到270的角度范围就行了,至于怎么回事,有公式慢慢理解,附上代码:
/**
* 现在总算看穿了,一切电商的阴谋,都是骗人的,电商可以显示的设置你转盘的结果
*
* @param luckyIndex
*/
public void luckyStart(int luckyIndex) {
// 每一项的角度大小
float angle = (float) (360 / mItemCount);
// 中奖角度范围,因为指针是朝上的所以范围是在210-270,这里要一个插图才能明白啊(6)
float from = 270 - (luckyIndex + 1) * angle;
float to = from + angle;
// 停下来是旋转的距离
float targetFrom = 4 * 360 + from;
/*
*
* 这里有点绕,等细细评味
*/
float v1 = (float) (Math.sqrt(1 * 1 + 8 * targetFrom) - 1) / 2;
float targetTo = 4 * 360 + to;
float v2 = (float) (Math.sqrt(1 * 1 + 8 * 1 * targetTo) - 1) / 2;
mSpeed = (float) (v1 + Math.random() * (v2 - v1));
isShouldEnd = false;
}
13.SurfaceView 辅助方法和MainActivity代码
这里没有什么贴别的就是对一些方法的调用,在这里我附上一些附加的方法,相信你一看就懂
public void luckyEnd() {
mStartAngle = 0;
isShouldEnd = true;
}
public boolean isStart() {
return mSpeed != 0;
}
public boolean isShouldEnd() {
return isShouldEnd;
}
下面就是MainActivity的代码了
public class MainActivity extends Activity {
private LuckyPadView id_luckypadview;
private ImageView id_imageview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setListener();
}
private void setListener() {
id_imageview.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (!id_luckypadview.isStart()){
id_imageview.setImageResource(R.drawable.stop);
Random random=new Random();
id_luckypadview.luckyStart(random.nextInt()%6);
}else {
if (!id_luckypadview.isShouldEnd()){
id_imageview.setImageResource(R.drawable.start);
id_luckypadview.luckyEnd();
}
}
}
});
}
private void initView() {
id_luckypadview=(LuckyPadView)findViewById(R.id.id_luckypadview);
id_imageview=(ImageView)findViewById(R.id.id_imageview);
}
14.最后附上XML里面的代码,很简单,一看就懂
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
>
<com.fat246.view.LuckyPadView
android:id="@+id/id_luckypadview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:padding="30dp"
/>
<ImageView
android:id="@+id/id_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/start"
android:layout_centerInParent="true"
/>
</RelativeLayout>
最后我把源码放到了网上,长期有效。
源码地址
文章
XML · 前端开发 · Android开发 · 数据格式
2015-08-15
这些年,我工作上走过的路
毕业季
2008年12月,清晨的阳光有气无力的铺在了一片没有经过打理的草地上。有了阳光,没了打理,自然成了杂草儿们的天堂,千奇百怪任性长着。阳光顺着草地,扶着墙翻过窗子,跃进了阳台。阳台左边是洗手间,前方是一个虚掩着的门,到了这,阳光止住了它慵懒的脚步。大学的宿舍是长方形的,四个边角都放着写字台和床。说是床,其实就是写字台上的一个挡板。海南蚊子多,所以挡板上都是有蚊帐的,蚊帐带来的坏处是,如果蚊子进去了,它除了烦你也别无出路,好处是,可以形成自己的一个小牢笼。我睡眼惺收,散发着一丝颓废的气质。颓废在这是一种赞美的词,只有艺术家才能配的上颓废这个词。当然,我不是艺术家,所以放在我身上,也就是真实颓废本意解释了。很快,我会热血的成为几百万即将毕业的学生,被学校撒豆成兵,最后可能成为炮灰。
我确实成为了炮灰。
刚从深圳铩羽而归,原本希望能在深圳找份工作,但落了空,完全没有门道,愣头愣脑的去了,愣头愣脑的回来。期间十几天,一个面试的机会都没有捞到,深圳现在也是完全不记得什么样子了。
这种感觉,想必和艾伦第一次去和巨人战斗的情景是一样的。
框框框,有人敲门,我们没搭理,接着他在门外问:有个公司来学院招聘,是招计算机的,你们要不要去看下。
我饿了,真的饿了,我没做任何思考,便起床动身。原先困倦的大脑脑功率趋近于0,现在瞬间飙升。恩,是脑功率,不是荷尔蒙。
那个时候自己就像一只农村的土狗,只要有份工作就行。而且如果纯粹做个码农,我还是很自信的。大学期间我就已经定了方向,专攻Java,所以或多或少还是有些积累,毕设的时候用Java写了个程序,给磁盘建索引,用的Lucene,顺利过了毕设。没想到的是,我竟然因此和搜索结缘,一直持续到今天。
创业征途
突然来的机遇就是这么神奇,也让人感觉莫名其妙,说来就来了,你真的不知道为什么会来,但真的就是这么来了。
当机遇来临的时候,常常让人懊悔的是抓不住。但抓住了,也未必会让你成功。但是,这是一个不管成功或者失败,都让你收获满满的一次机遇。
这是一家创业公司。我去的时候一开始只有三个人,A君,B君,还有我。
A君多才多艺,产品经理+UI设计+投资人,B君和我则同为技术合伙人。
在找到我之前,A君和B君已经在海南师范大学旁边的一栋写字楼租了一个办公室。现在回想一下,或许我们应该租个地下室,成功的概率可能会更高些(不过,如果去美国的车库或者地下室,应该成功率又会提高)
就这样,08年12月开始了我程序员,或者是创业的征程吧。
我们做的是一个原创音乐站点。
这期间我们常常每天工作13-14小时。要知道,那个时候我们没做过任何项目啊,只是大学看了几本程序设计的书,而真正做出一个产品,需要掌握的东西却是是非常多的。
但是热血和时间这东西,真的是有用的。就这么着,基于热血的推动,时间的磨砺,渐渐的,一个原创音乐分享网站竟然慢慢开始开始有模有样了,整个过程,我们自己设计,开发,购买服务器,托管,运维,运营。
作为一个流媒体音乐站,不同于一个简单博客,尤其是对于我们几个没有什么技术积累的年轻人而言,其挑战是非常大的。
流媒体服务器,Web端Mp3播放器,音乐上传,音乐转码,音乐下载,个性化主页等等,挑战非常多。B君后期专攻页面和JS,我想,他现在能够开发Web端操作系统,也得益于这次的挑战的吧。
首先是站点搭建,没有采用Worldpress之类的开源组件,真的是从零直接从头开始做的。那个时候不知道原型设计工具Axure,A君就直接用Photoshop把界面画出来,然后把一些小ico切出来给我们用。A君虽然没有做过产品经理和UI设计师,但是他设计出来的界面真心大气(08年的时候),后续我们的对手,原创音乐中国,还有那有个因为涉黄被关闭的一个网站,都在我们上线后不久改版,我们对比过,明显借鉴了我们的设计元素。
我和B君大学学的都是Java,于是采用了SSH2开发。从头开始垒代码。还是那句话,如果只是做个类似博客平台的站点自然是不难,但我们做的是原创音乐分享网站。这里面涉及到如下几个难点:
首先我们是个音频流媒体网站,需要搭建流媒体服务器,以及播放音频的客户端
接着这是针对音乐人的一个社交网站,有点类似现在的微博,音乐人上传了大量自己的音频,需要大量的粉丝以及与之互动。所以需要提供一个酷炫的个人主页,QQ空间是一个标杆。
于是经过仔细研究,我们使用了Red5 搭建了音频流媒体服务器,实现了一个简易的 flash 音频播放器,暴露js调用接口,使用Js进行控制。
个人主页我们花费的精力非常多,09年时,我们就已经仿照QQ了,允许换肤,自定义布局,这个对前端以及后端要求都挺高的。后面B君在做后端服务的时候,也同时成了一个前端高手,我可以说,他已经精通CSS和JavaScript了。(后来B君来了北京,后续又去了香港,在香港时,已经可以用JS开发Web操作系统了,这是后话)。 整个页面任何一个版块,你都可以显示,隐藏,互换位置,设置不同模板。
作为一个流媒体网站,上传音频是个很重要的东西,为了显示上传进度,我们当时就折腾了好一会,接着遇到转码问题,使用了ffmpeg,设置要把转码进度也显示给用户看。
到后来,我们已经做了一个类似虾米这种音乐类的网站很多功能了,比如给出音乐列表,自动播放,直接在title上显示播放进度等等。
SSH2 给我的感觉就是慢,调用栈太深了。作为一个互联网应用来说,真的不适合。但是架构已经选定,也没办法。于是我们通过大量添加缓存的方式解决性能问题。
有大量的资源,自然就需要搜索了。为了方便查找歌曲,我们引入了基于Lucene的Compass。可以直接基于数据库建立lucene的索引文件。一般这种东西,很难用好,或多或少都有问题。我期间就经常各种错误,各种问题,不堪其扰。
到10年的时候,我们的歌手用户只有几千。但是歌曲数量以及总文件大小已经相当可观了。我们在各个细节进行了非常大的努力,比如歌曲,用户评论,可见不可见,上传进度,页面设计等等。
产品我们觉得问题不大了,可是,可是我们该怎么推广运营呢?完全没有门道。而且从08年到10年,差不多一年半,我们是完全没有盈利的,只是在烧A君的钱。
如果有了一定的用户量和活跃度,其实关于盈利的形态我们都是想好了的,当然这个也是借鉴国外的模式。而且我到现在都认为是一个好的模式。
但是我们感觉我们快撑不到那个时候了。我们那时候也没有投资人的概念。在海口,互联网行业是相当不发达的。不像现在,你拿个想法,跑到咖啡厅,就有人会听你讲。
于是我们展开了自救行动。10年团购很火,于是,我们打算先暂时放下手中的主业,做起了团购。但是后面我们才知道,团购初期是很烧钱的,不是我们能玩的起的。
海南是个旅游大省,于是我们开始做旅游站,但是啊,旅游要有线下资源。
急了就什么都尝试了。
2010年10月国庆期间,基本上宣告了创业的失败。海口有个奇特的天文现象,就是凡是国庆七天,那是必然下雨的。这个国庆也不例外,整整七天都是雨。我当时是个E站的一个老用户(只是看文章的老用户),看到了E站正好招人,但那个时候招的是Ruby程序员。于是十一前的一个礼拜,我弄了一本敏捷开发的书,然后把58同城的UI扒下来,然后用ruby on rails 写了站点,包括抓取,包括显示等。当然,没有后台。整整做了一个礼拜,然后把它作为示例代码打包,发给了E站。 后面就接到了E站老大的面试电话。
E站的老大看了我的代码后,让我很顺利通过了电面,问我是否可以直接去北京。我当时心想,偶像就是偶像,看代码就能看到我的能力以及诚意,没有N轮面试的折腾。
走的那天,好像还是下着雨。当时我的生活已经非常窘迫了,一个好朋友赞助了我机票钱,我也在没有找到北京住处的情况下,就踏上了北京的航班(临走的那天,B君说他也在北京,可以给我落脚,当时那个感动),开始了我现在的工作。
北京的旅程-开源之路
起源
2010年10月15日,我正式在C公司入职。E站是C公司的一个子站点,用RubyOnRails 开发的。我一开始基于其上做开发。就这么做了一个月Ruby程序员,有一个事情就第一次改变了我的职业方向。
某天老大突然说,你不是以前用Java做过搜索么,公司主站点的搜索就交由你做吧。当时公司的搜索是外包给一家专门的公司做的。我自然很乐意的接受了。
后面经过一个月的开发,也就是2010年12月中旬时候,第一个版本上线了。当然这个版本可以说超级简单,就是用Lucene搭建了一个单机版本的搜索服务。老大很开心,还请我们去看了让子弹飞。
Web/ORM/ODM 一站式开发框架
因为我之前做过一段时间的Ruby程序员,一对比,我发现,Java的Web框架都太不好用了,Java的ORM框架也不好用,Java 的MongoDB Client 也不好用。于是我决定开发一套一站式Web框架。
正好除了搜索的任务以外,公司希望做一个全站通用的标签系统。当时在选型上,老大说用Spring,我当时说,给我点时间,我自己开发一套开发框架出来,老大说,你有信心做好么,我说有。老大给予了肯定的答案。
于是,2011年的有一段时间,每天早上六点我准时起来,光着膀子开始写代码(这个可以推测是夏天了)。写了一个Web框架,一个ORM框架,一个MongoDB的 ODM。 后面我问老大,名字叫什么好,老大说,就叫ServiceFramework。于是我便把Web框架叫做 ServiceFramework,ORM框架叫做 ActiveORM,MongoDB的 ODM叫MongoMongo。经过公司许可开源了出去。
相关介绍参考:
http://allwefantasy.iteye.com/blog/1631522
http://www.iteye.com/news/25793
项目地址:https://github.com/allwefantasy/ServiceFramework
从2011年开始,我一直在优化这个项目,到现在已经四年了。不过开源版本因为工作缘故,一直没有更多精力维护,并且无法进行推广。但是内部版本却获得长了长足的发展,作为一个针对API服务开发的框架,除了易用性以外,我一直重点做的是可运维。目前我所在的数据部门,已经完全使用了这套框架。
关于可运维,框架自身可以做到的:
接口 QPS 监控
接口平均响应耗时监控
接口调用量(如果是http的话,则是各种状态码统计)
内置HTTP接口,外部可通过该接口获取以上的数据
同时,框架日志信息输出默认包括:
http请求url
整个请求耗时
数据库耗时(如果有)
响应状态码
还有一个比较核心的功能是,服务降级限流。 ServiceFramework主要面向后端服务,如果没有自我保护机制,系统很容易过载而不可用。经过一定的容量规划,或者通过对接口调用平均响应耗时的监控, 我们可以动态调整 ServiceFramework 的QPS限制,从而达到保护系统的目的。
这里其实还有一个重点是,传统的方式是,容器包含应用,比如开发好的代码打成war包放到tomcat等容器中。新框架的方式是应用包含容器(内嵌jetty),一个应用服务就是一个纯粹的java application.
开发完这个框架后,我花了半个月时间完成了公司标签系统的开发。
索引系统 (暂时未开源)
当时老大看上了一个叫ElasticSearch的搜索项目,类似Solr。不过那个时候ES还非常的初级,我当时年少气盛,没经过任何测试就上线了,但是对其了解有限,上线后问题不断,当时索引量已经颇大,光索引文件就达到百G。一开始我将原始数据也store进了索引,瞬间IO飙升,服务直接挂掉了。接着我讲原始文件剥离,只存储索引文件,但是发现也扛不住,服务经常因为压力大而没有响应,因为ES有Failover功能,如果发现有服务当掉了,就进行分片副本迁移,直接把网络带宽占满(那个时候路由器都是百M的)。我就开始看代码,改代码,但是不得要领。当时ES的社区远不如现在活跃和成熟。
我后悔了,不应该没有调研测试清楚就上线。而且当时一直困扰我的就是数据更新问题。索引了后,数据很快变更,我这边必须想办法得到通知。
于是我决定另起炉灶,开发一个简化版本的ES,使其完全能被自己掌控。经过一个月的开发,第一个版本的我们称之为CS的项目完成了。后面使用CS替换掉了ES。服务再也不会轻易当掉了。
在一次ES的分享会上,我分享了CS。蛮多人还是感兴趣的。CS作为一个分布式索引服务,特点有:
分布式架构
支持索引数据分片
支持构建离线全量索引过程中合并线上新增数据的机制
支持数据热备
拥有一个剥离配套的统一查询引擎
支持模块化,组件化
关于这个项目的一些简单介绍,可参看:
http://pan.baidu.com/s/1i3qsoBF#path=%252FESCC%25233&render-type=list-view
中的 ES分享.pptx 文件
CS的核心是是查询引擎和索引存储剥离。一些高级功能,比如跨索引检索,结果二次排序,摘要提取,获取详细内容展示,都是作为模块放在查询引擎中。当然统一查询引擎最核心的意义还是在于可以快速更新二次排序的引擎。当然这还需要有一些其他架构做支撑。
有新的模块添加,只需重启查询引擎,而不需要重启索引存储服务。索引文件重新打开是非常消耗CPU,IO的,常常会造成机器负载瞬间飙升,导致很多搜索维护人员轻易不敢重启服务。在CS不存在这个问题。
12年的时候,我终于有伙伴了,以前一直都是一个人孤军奋战。我称为C君。C君后来参与搜索的优化。之前我因为对Lucene的一个误解,导致排序效果一直很烂,我一直不得要领。C君发现了这个问题,瞬间搜索排序就正常了。后来C君开发了一套新的分词引擎,现在已经是一个知名的分词套件了(可参考:https://github.com/NLPchina/ansj_seg)。基于这套分词引擎我们做了很多分析方面的工作。
索引服务这个项目前前后后花了我四年多的时间。现在策略层公用的组件已经包括 SQL,JSON,URL params,Mutil-Index,数据网关等。后面我一直从事数据架构和算法方面的专研时,索引服务也成为架构中非常核心的一个服务。
北京的旅程-大数据架构之路
到12年年末为止,应该说我做的大部分是项目层级的工作。13年年初,公司突然开始重视大数据了。于是我们部门人员由原来的两个人变成了五个人。加了两个刚毕业的学生D君和E君以及一个算法工程师F君。
F君也是对我影响很大的一个人,让我从此了解了机器学习,算法方面的东西。我是一个比较容易受我认为优秀的人影响的,我会去学习他的思维,他的做事风格。
C君则对大数据平台有推动的作用。当时公司还没有数据平台,C君推动公司购买了五台配置很低的服务器,直接放在了办公地点。然后我们一起搭建一个简单的Hadoop集群。
关于大数据平台,因为我经历过从无到有,到最后离不开它的整个过程,我形成了一套自己的观点。而这些观点,我放在了一次PPT分享里。这里可以提两点:
大数据平台(hadoop/spark),真的不要被‘大数据’这个词给吓到了。它大数据都可以处理,小数据当然是小菜一碟。本质上大数据平台是一种解决问题的范式,一个通用的分布式计算存储平台。想象一下,把一个文件丢进平台里,然后在Web上写个SQL就可以从各个维度分析这个文件里的数据了。整个过程可能只要花你几分钟,这比你花半天写个单机程序统计来的快吧。SQL一不小心写多了,设置成定时,就成BI报表了。
大数据平台搭建维护很贵吧?真不贵。大数据平台谁说了一定要大?一定要几百上千台机器了?没人说一定要这样子吧。我们当时5台机器,在百万数据集就能运行的很好了。什么样的数据量,使用什么规模的集群。当你的价值体现出来了,公司自然也会毫不犹豫的给你加机器了。
我们当时完成的第一个项目是EDM,邮件精准投递。当然这个项目的前提是需要对人有一个画像。于是我们几个人加上产品头脑风暴了好长一段时间,人工定义了上百个属性,加上一个由词构成的属性集合(14000维度),得到了对人的维度表征。邮件的话则直接使用14000维的词特征表示。
假设人的特征集合是A,邮件的特征集合是B,目标值为0或者1
0 表示不会打开邮件
1 表示会打开邮件
f(A,B) => 0|1
典型的一个二元分类或者逻辑回归问题。
做完后邮件打开率提升还是很显著的。当时我们想尽办法推测出用户的年龄,技能,职位等各种信息。有一些属性的预测取得了相当不错的效果,比如用户年龄,我们拿同事的账号进行年龄预测,上下浮动不超过2岁。
但做完这项目后C君,F君相继离开了,但这个是我数据道路上的一个开端。13年年中,来了K君。K君之前是某大型视频网站的数据部门负责人。应该说,通往数据道路康庄大道由此完全打开。此时公司战略上也开始支持数据部门,人员迅速扩充到15人左右,包括数据分析,开发,算法。不过实习生占了很大一部分,服务器方面,新采购的单台都至少24+核,32+G 内存。
13年到14年是一段非常辛苦的路。在这两年我实现了快速的成长,开始了大数据平台架构之路,同时也投入了相当一部分时间在机器学习和数据研究之上。
应该说,公司的数据平台是完全从0开始的,我和K君对整个平台做了比较详细的规划。
第一步推动公司进一步扩充了集群。我们也开始了自己的第一个目标,构建公司自己的BI系统。
K君亲自制定了数据上报格式规范。我则基于Hive使用其SerDer机制开发了一套数据格式解析器。支持通过JSon格式文本描述日志格式,然后自动映射到Hive表中。这样新来一个格式的数据,你只要填写一个json格式的描述文件,就能使用Hive直接进行SQL查询。
另外指导一个实习生开发了日志接受服务。同时L君则改写了阿里开源的一个宙斯平台。基于该平台,可以直接在Web界面管理和调试Hive/MapReduce任务,并且设置调度任务。
完成了这些工作,我们构建起了数据分析平台的雏形。在宙斯添加大量的SQL统计脚本,计算结果导入到MySQL中,然后在Web端展示,完成了BI报表的任务。当然这只是一个离线计算平台,真正和应用平台打通,形成完善的数据流入流出,需要做的工作还太多。
从那个时候起,一直到2014年11月,我们终于完整的构建了一个数据支撑平台。
Hadoop/Spark/HBase 体系,支撑BI,数据离线分析,推荐协同计算等
分布式索引服务,支撑搜索,数据平台供查询数据的存储
统一查询引擎,为数据产品提供统一的查询接口
内容网关+数据网关+上报,打通产品到数据平台的入口。
分布式缓存体系(Redis) ,可支持推荐系统,数据网关等产品
初步的服务监控体系 (参看:https://code.csdn.net/allwefantasy/platform 的介绍)
推荐系统,支持相关内容推荐,用户个性化推荐,公共队列展示,底层完全基于Redis实现。这里有个给兄弟公司介绍的一个PPT(http://vdisk.weibo.com/s/HZFjdG-haPc)
配置与发布系统(运维相关)
基本上在这个过程中是去数据库化的。HBase做数据存储,分布式索引服务则将数据进行索引,支撑复杂查询,统一查询引擎则以一致的接口对外提供查询服务。
关于这两年做的架构,我未来专门写了一篇文章进行解析。
平台是你顺利开张其他工作一个很重要的基础设施。当你有了一个完善,易于扩展的平台,你的工作会越来越少,新添加的东西会越来越轻量。你会发现,啊,原来这个都已经是被平台囊括了,啊,只是加个模块嵌入进去就完事了。
后续谈到的机器学习,很早之前其实我也接触过,搞个SVM lib弄弄,都是C的,单机的,生成个个是数据都费事,更别说后面的训练跑了。有了平台后,真的就看你的自己的聪明才智了,你更容易获得你想要的数据,你更方便将数据转化为你需要的格式,你更容易快速看到你的算法在实际数据集上的效果。
举个简单例子,我们做了一个智能问答的项目,在现有平台上做,只花了两天多时间。如果没有平台,我智能呵呵了,一个礼拜都不一定能做顺畅了。
北京的旅程-机器学习之路
2014年开始基于Yarn平台引入Spark。因为内部使用的是CDH4.4.0 版本的Yarn,Spark默认支持有点问题,所以必须自己下载源码,修改一些地方才能编译通过。Spark 平台是对数据分析师,算法工程师们的恩赐。我之前做一个数据调查,或者是为了产生一个训练文件给算法用,可是颇费周折。Hadoop的开发部署发展了这么多年,其实还都是挺麻烦的。用Spark几行代码就可以搞定,而且本地IDE写好,直接黏贴到spark-shell交互式运行。
对于算法工程师而言,原先比如使用贝叶斯之类的,测试只能跑个小数据集,不然很久都跑不完啊,跑一个礼拜没出结果也是常事。有了Spark之后,测试的时候就直接在一定规模的数据上跑了。真实的数据都在集群上,省去了要下载数据到本地的问题。要知道,目前大部分算法本质上是通过大量的数据通过一些优化算法提取出目标函数的参数,算法的这种形态决定过了只有在一定规模的结果集上才能如实得到实际数据集上的效果。而Spark则很好的满足了这种需求。
Scala 是我比较愿意接受的第三个语言。第一个是Java,从大学开始就一直用着,第二个是Ruby,有过一段短暂Rails程序员的经历,也从中得到很多灵感。现在的话,基本是Scala,Java混合编程。Scala 确实能够有效的提高编程效率,而且可以和Java无缝操作。在公司内部系统中,基本上都是混合着用的。
程序员再也不应该仅仅是写代码让服务跑起来或者设计一个架构做到良好的扩展性,这些工作本质上是重复性的工作,你很难做到和别人不一样,所以才会有码农,你只是垒代码。随着计算能力的提高,以及机器学习的发展,程序员的目标应该更加高尚,我怎么才能让你在茫茫人海中找到多看了你一眼的人?
前面我提到,在做架构的过程中,我对数据分析和机器学习也非常感兴趣,期间也做了一些成果。虽然主要精力还是在架构上,但经常会抽出时间做相关的研究。
新词发现是一个非常有意思的领域,用途非常多。譬如可以构建垂直领域词库,自动发现新热门词汇。词库的重要性我不用强调了。基于Spark强大的计算能力,我直接对200万+ 的博文进行了分析,得到大概八万词,包含 中文,英文,中英文混合词。通过凝固度,自由度,词频,idf,以及重合子串(比如 c1c2c3..cN c2c3..cN-1 这种形态的 我们认为是重合子串,如果词频一样,则都过滤掉,否则留词频高的)五个维度进行阈值设置和过滤。事实上,中间结果可以到几百亿,一个不小心就可以把Spark跑死,但是也在这个过程中慢慢对Spark有了更深的理解。 最终效果还是不错的,现在它已经作为我们的基础词库了。
算法自然是参考论文的,但我感触比较深的是,通常某篇论文只会在一个视角去focus 某件事情,所以你需要参考多篇,从不同角度去理解这件事情的解决方式,最后通过实验综合,得到一个更好解决方案。我参考了两篇论文,比如凝固度,自由度是出自一篇论文,而重合子串则来自另外一篇论文,然后自己观察实际数据,添加了很多规则,才得到最后的结果。
新词发现这个我足足做了两到三个礼拜,能有这么多时间focus在这件事情确实不容易。在公司,有太多的工作和新的想法需要去实施,尤其是公司在快速的转型或者上升期。我业余时间虽然不加班,但是我常常会在脑海里把系统架构方面的东西不断排练,就像大脑是个虚拟机,模拟器,一整套处理流程都会在大脑里事先跑起来,这对自己掌握某个系统,或者是对全局的掌控非常重要。
一说到算法,大概很多人心里就是想着,恩,我把数据转化为算法需要的格式,然后丢给现成的算法跑,跑着就出结果,或者出模型,然后反复尝试,直到得到你认为能接受的或者最优的结果。我一开始也是这么想的,可是如果你真的做这件事情,就发现完全不是那样子啊,需要注意的细节太多了。
新词发现没有现成的工具包,所以完全自己写了。第一步,你要获取语料。这容易,基于现有的平台,我从我们资源中心挑出了200万篇文章id,然后根据id到数据网关获取title,body字段。这个基于现有的平台,也就一个SQL + 几行Scala代码就搞定的事情。
SQL 其实就是用Hive 生成一个200万博文id列表。 Scala 代码看起来像这个样子
sc.textFile("/hdfs-upload-dir/20140904-114357-result.txt",30).flatMap{f=>
val Array(id,name,skill) = f.split("\t")
getFromUrl(id,"blog","title,body")
}.filter(f=> f.length > 100 ).saveAsTextFile("/output/william/newwords/articles")
因为我们的新词发现是没有词典的,需要枚举所有组合,然后通过一定的规则判定这是不是一个词。比如 ‘我是天才’,就这四个字,组合有,‘我是’,‘我是天’,‘我是天才’,‘是天’,‘是天才’,‘天才’ 。你想想,200万篇文章,这种组合得多夸张,问题是你还要接着给这些组合做计算呢。这个算法可没告诉你怎么处理的,你只能自己去想办法。看到了,真正你做算法的过程中,不只是实现,你需要面对的问题特别多,我是怎么做的呢?
将所有html标签替换成空格,通过小空格切分后我们就能得到无数的小文本块,然后我们就能做词枚举了
一个词最长不能超过5个字
将中文,中英文,英文分开来做
将一些特殊字符 类似‘!¥……()+{}【】的呀啊阿哎吧和与兮呃呗咚咦喏啐喔唷嗬嗯嗳你们我他她这是由于’ 这些不可能成词的字符先去掉
这样,词集合就小多了。 接着就是按论文里的规则做计算了,比如算词的凝固度,算重合子串。这里面还会遇到很多性能,或者内存的坑,比如Spark里的groupByKey,reduceByKey。 我一开始省事,用了groupByKey,歇菜了,内存直接爆了,为啥,你要去研究groupByKey到底是怎么实现的,一个词出现几十万次,几百万次都很正常啊,groupByKey受不了这种情况。所以你得用reduceByKey。
很好,实现了算法后得到了结果,可人家没告诉你,他贴出来的结果都是好看的,那是因为他是按频次排的,但如果你拉到最后看,结果就不太好看了。这个时候你就需要观察数据了,然后提出新的规则,比如最后得到的中文词结果,我用了下面的规则过滤了下
f._1.contains("和")||
f._1.contains("或")||
f._1.contains("就")||
f._1.contains("将")||
f._1.contains("是")||
f._1.contains("的")||
f._1.contains("为")||
f._1.contains("个")||
f._1.contains("到")||
f._1.contains("来")||
f._1.contains("种")||
f._1.contains("中")||
f._1.contains("length")||
f._1.contains("tmp")||
f._1.contains("void")||
f._1.contains("如")||
f._1.endsWith(":")||
f._1.endsWith("amp")||
f._1.endsWith("[")||
f._1.endsWith("]")||
f._1.endsWith("’")||
f._1.split("\\s+")(0) == "if" ||
f._1.split("\\s+")(0) == "for"||
上面的规则是什么意思呢?凡是词里面包含‘或’的,或者'就'的或者上面罗列的,我都认为这个词是没有意义的,经过这个简单规则一过滤,效果好非常多,很多没什么意义的生活词,或者不成词的词就被去掉了。中文,英文,中英文混合,我都加了很多这种规则,最终才过滤出了八万计算机词汇。
在多分类的算法中,贝叶斯和SVM的效果是相当不错的,Spark中朴素贝叶斯的实现基于该论文: http://t.cn/RPkPkJq ,我看了一遍,在我的数据集上跑的效果并不好。我修改了论文中 p(t|c) 的公式 为 p(t|c) = 出现t的c分类下的文章数/c分类下得所有文章数 对于t|d=0的情况不参与计算。然后舍弃采用Log将乘除转化为加减。分类效果得到明显提高。
我把它应用在了代码篇的语言判断上。给我一段代码,我告诉你这是什么语言写的。一开始识别率就达到70%左右,后面通过提高维度,将代码片高维表示后(65万),准确率提升到了85%。总体而言,贝叶斯和SVM后面有人提出了各种改良,但是最后是改良了还是改差了,还得看具体数据,你需要有调整他们的能力。
再说一个应该说非常具有实际意义的一个算法:职位和简历匹配度算法。当然设计的是为了招聘研发,所以基调就是以技能模型为主,提出一套完整匹配度算法。效果不错。之后的研究人员基于我的算法,添加了职位方向等因子,使得效果更进一步。这套系统会成为后续招聘领域比较重要的基础。
人生第一次跳槽
在上一家公司工作了五年之后(2015年8月),我终于跳槽了。当时老板(我们老板是一个非常优秀的老板)请我吃饭的时候,问我为啥跳槽,我说我想开始做些事业。我能呆一家公司呆五年,说明老东家是真好。
我到了L公司之后,初期主要是实时计算这块。实时计算能做的事情还是非常多的。这让我后续以Spark Streaming 为起点,深入研究Spark 内部源码做好了铺垫。
此时我除了负责一个应用开发团队,同时还负责内部的机器学习团队。然而我是一个很懒的人,不太适合带团队,我觉得我需要太多的时间投入到技术上去,去专研。所以团队发展不足,这也让自己很愧疚。然而只要和我工作过的人,我一定会让他们有技术上的收获。和我一起共事的同事有的去了百度,有的去了滴滴,大部分都还在互联网公司。有一次吃饭的时候,他们有人说,虽然我很严苛,但是确实给他带来了收获。听到这一点,我还是很开心的。技术人员应该以技术为纽带,互相帮助对方去提升。
这次跳槽唯一的遗憾之处在于,8月3号办完离职,8月4号就开工上班,当天就加班了,ORZ。
Spark 的再相会
经过八个月业余时间(周末加上工作日夜晚)的努力,我想要的产品原型终于做成了。然而终究是计划有调整,在主动和一些CTO,资深技术人员,业已创业成功的人聊,加上碰了一些壁之后,我打算放慢些节奏。似乎光有个原型是不足以打动投资人的。
这个时候,Spark 对我的吸引力突然无限超越了事业对我的吸引力,所以三个月时间我写了20几篇Spark相关的文章,并且推动部门上了很多Spark Streaming相关的应用。
在此期间,我大量阅读源码,并且修正了不少在特地情况下Spark会工作不正常的错误,同时做了一些增强。这些工作在Spark Streaming 的玫瑰与刺 有所提及。同时还提出了一个 流式计算动态资源调整算法
2016年3月份开始,我慢慢将工作重心放在多维查询上,大体朝着SparkES 多维分析引擎设计 努力。5月份左右开源了StreamingPro项目,涵盖了批处理,流式计算,交互式查询等多项功能。其实原理也简单,基于之前开源的ServiceframeworkDispatcher 实现配置化,使用以前开发的Serviceframework框架嵌入到Spark Driver 中,这样就可以接管Spark UI的工作,提供新的交互接口。
技术的轮回
最近基于Spark Streaming + ElasticSearch 做数据的日志分析。还记得我12年(也可能是11年)引入ES做搜索,那个时候简直被虐死,所以开发了一套自己的索引系统。没想到四年后又遇到它了,而这个时候不在是在搜索领域,而是在大数据日志分析领域。人和技术,都存在某种轮回。
文章
分布式计算 · 算法 · 大数据 · Spark · 索引
2016-09-09