Elton's Blog

Author Archive

Nginx使用Linux内存加速静态文件访问

by on 二.05, 2012, under Linux, Web

Nginx是一个非常出色的静态资源web服务器。如果你嫌它还不够快,可以把放在磁盘中的文件,映射到内存中,减少高并发下的磁盘IO。

先做几个假设。nginx.conf中所配置站点的路径是/home/wwwroot/res,站点所对应文件原始存储路径:/opt/web/res

shell脚本非常简单,思路就是拷贝资源文件到内存中,然后在把网站的静态文件链接指向到内存中即可。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/bash   
 
res_path="/opt/web/res"  
mem_path="/dev/shm/res"  
lk_path="/home/wwwroot/res"  
 
if [ ! -d "$mem_path" ]; then  
        cp -r "$res_path" "$mem_path"  
fi  
 
if [ ! -L "$lk_path" ]; then  
        ln -s "$mem_path" "$lk_path"  
fi
Leave a Comment :, , more...

可伸缩性架构常用技术——之数据切分(Data Sharding/Partition)

by on 一.29, 2012, under Database

1. 简介

我们知道,为了应对不断增长的数据,我们对数据进行切分,存储在不同的数据库里,本文提到的数据库在非特定指明的情况下,均指一个逻辑数据库(是一组数据库,比如Master-Slave),而非单一各个物理数据库。

其主要有两种方式:

垂直切分(Vertical Partition/Sharding):就是把不同格式的数据,存储到不同的数据库。
水平切分(Horizontal Partition/Sharding):就是把相同的数据格式的数据,存储到不同的数据库,本文将侧重这点进行讲述。

2. 垂直切分(Vertical Partition/Sharding)

对于垂直切分,其实应用非常广泛,主旨是把那些关系依赖非常紧密的数据保存到同一数据库,主要包含如下几种应用方式:

不同的应用使用不同数据库:这个非常容易理解,即对于一个企业来讲,往往有多个应用,甚至有些应用逐渐演变成两个或者多个应用,这其实就是一种垂直切分应用。
应用的不同模块使用不同的数据库:为同一应用的不同模块分别使用不同的数据库,之间提供低耦合的API进行访问。
同一应用相同模块使用不同数据库:在一些应用中,对于适合关系查询的数据,保存在关系数据库,而另外一些适合以NoSQL数据库保存的数据(例如key-value数据库),保存在NoSQL数据库中,方便数据扩展。这里给出一个例子,比如一个论坛应用,可以对个人用户信息保存在关系数据库,例如,其访问次数,个人信息等等,而对于发表的帖子和回帖,则可以保存在一个NoSQL里,方便扩展。注意,这里给出的例子并非真实例子,只是为了易于说明给出一个假设性的例子。

3. 水平切分(Horizontal Partition/Sharding)

水平切分相对比较复杂,我们还是从水平切分的策略谈起。

3.1 水平切分策略主要分为以下几种:

1. Round-Robin(轮询式)算法

顾名思义,就是把数据按照轮流的方式依次存放在的数据库节点上,比如,有2个节点,N0和N1,那么Data0放在N0节点上,Data1放在N1上,Data2放在N0上,依次类推……。

这种方式实现起来非常容易,对于数字键,我们有:n = key mod N。其中,key为数据的键,N为节点的数量,n为存放数据的节点编号;对于那些非数字键,我们可以让其转变为数字键,比如通过某些hash函数,让键值均匀分布,于是有:n = f (key) mod N。

这种方式有个缺点非常明显,不容易应对数据节点的变化,即不易进行二次切分。所谓二次分片是指,当数据的增长超过数据库容量时,需要增加数据库,或者系统故障导致某些数据库不能使用时,这时需要重新切分数据库。例如,有两个节点,N0和N1,现在需要增加一个节点N2,这时候,都需要吧N0上的数据和N1上的数据迁移到N2上,这个工作量是巨大的;并且可能导致上层应用对数据的改变,比如,之前数据Data5存储在N1上面,上层应用访问该数据时,根据key=5知道其存储在数据库N1里,那么便会在N1里查询数据,现在增加另外一个节点N2,那么这条数据被迁移到N2上了,上层应用就应该去N2上查询此数据了,这个看似简单,其实往往导致应用程序的复杂性很高。

2. 虚拟分片技术

为了保证二次分片时,避免对上层应用因为实际物理数据库发生改变而引起对数据访问逻辑的改变,中间加入了一个虚拟片段—物理片段映射表,数据对象存储在虚拟分片上,每个虚拟片段通过这个映射表找到相应的物理片段。这时间,上层应用依赖于虚拟分片,而非物理分片,只要保证虚拟片段足够多,就能避免上层应用的依赖。

3. 一致性Hash算法

为了避免数据库数量发生变化,引起大规模的数据迁移问题,而引入了一致性Hash算法。此算法由David Karger等人发表于1997年,论文题目为《Consistent hashing and random trees: distributed caching protocols for relieving hot spots on the World Wide Web》,这里有一篇文章讲述Java语言简单实现一致性Hash算法http://weblogs.java.net/blog/2007/11/27/consistent-hashing。

一致性Hash算法的主要思想是不改变Hash函数本身,当减少节点时,临近的节点接手该节点,因此,消失节点上的数据迁移只迁移到临近节点上面;而增加节点时,只接手其临近的一个节点的部分数据,因此,只有一部分临近节点的数据被迁移至新加节点。

我们来详细了解一下具体实现:我们的Hash函数生成的数据都有一个值区间[min,max],我们把该区间用一个环来表示,每个节点的hash值都映射到这个环上,如下图所示:

假设我们的值区间是[1,12],我们有三个节点,1,4,9,数据的键也映射到这个环上,a的键值介于1~4之间,则存储在节点4上,即按照顺时针方向存储数据,同样b存储于节点9,而c存储于节点1。

假设节点4不可用时,那么数据a就会被迁移至节点9,其他节点的数据不发生迁移,如下图所示:

假设增加节点7,那么将把节点9上的部分数据迁移至节点7,其他节点数据不发生改变,如下图所示:

4. 按照数据的特点进行切分数据

最常见的就是按照地理位置切分数据,那么我们按照用户的注册信息或者用户数据提交的ip地址等来把它们放置于离它们地理位置最近的数据库中。

3.2 实际应用

在真实的应用中,往往会结合这些策略,甚至提供更为抽象的接口让开发人员实现适合自己的切分方法。我们这里讲述Mongodb和Hibernate Shards的分片方式。

3.2.1 Mongodb Sharding

Mongo db是基于文档的NoSQL数据库,查询方式和关系数据库非常接近。

Mongodb把数据存放在称为Chuncks数据结构上面,Chunck的默认大小是64M,每个Chunck上面存储一定切分范围的数据,当数据超过64M时,会自行分裂成两个Chunks,相当于一致性Hash算法添加了一个节点,只是这个节点不是DB。而每台物理db(称为Shard)上含有多个Chunks,为了达到更好的负载均衡,这些物理db上的Chunks会自动迁移,使得db上的Chunks发布均衡。

3.2.2 Hibernate Shards

Hibernate Shards是在Hibernate Core上做的一层扩展,目的是在关系数据库上封装和降低水平切分的复杂性。

Hibernate Shards为开发者提供了抽象接口,开发人员可以实现自己想要的切分策略,为了避免物理数据库发生改变引起应用程序的改变,其采用虚拟分片技术。

Hibernate Shards参考中文文档请参见:http://redhat.iteye.com/blog/328032

3.3 应注意的问题

水平切分数据库之后,会给查询造成一定的困难,特别是Aggregation查询。Mongodb采用Map/Reduce方式,能够比较高效进行Aggregation查询。

4 总结

对于大规模,可伸缩,海量数据的应用,数据切分是其架构必须考虑的一个重点内容,我们在进行数据切分时,往往采用先垂直,再水平方式对数据分片。

摘自:http://blog.csdn.net/co0der/article/details/7217974

Leave a Comment :, more...

Bangkok春节游

by on 一.27, 2012, under Photography

趁着春节长假,抽空走马观花看了两个东盟邻国。
24号清晨坐上开往机场的MRT,中午时分到达曼谷。

“曼谷”(Bangkok)是泰国首都,东南亚第二大城市,主要港口和政治、经济、文化中心,被誉为是“佛教之都”。这是一个充满矛盾的地方。一方面,作为东南亚第二大城市,曼谷高楼林立,轻轨和高架桥在钢铁丛林间蜿蜒,商务区的白领步履匆忙,一面抱怨着高峰时段的交通堵塞,一面筹划着周末的欢乐时光;另一方面,数以千计的大小寺庙仍然遍布街头巷尾、高楼大厦间,仍然深深影响着每个人的世俗生活和精神世界,使它无愧为“黄袍佛国”的首都。这里的人仍旧谦和友善,并且不吝以最直白的方式——黄衫——来表达对国王的衷心爱戴。


入住酒店Bhiman Inn旁边的泰国小寺庙


走到湄南河边


湄南河又名昭披耶河,是泰国河流中水量最大、长度最长的河流,有泰国“河流之母”之称。湄南河全长1352千米,纵穿泰国东南部,流经大城,贯穿曼谷市区,在城市交通运输及岸边居民生活中扮演着重要角色。


这个就是在湄南河上穿梭的水上公交车


曼谷Downtown,车水马龙啊。


大王宫始建于1782年,紧偎湄南河的大王宫位于曼谷市中心,占地22万平方米。这里共有22座错落分布的古建筑群,汇集了泰国建筑、绘画、雕刻和园林艺术的精粹,是泰国曼谷王朝一世王至八世王的王宫,又称“大皇宫”或者“故宫”,也是曼谷保存最完美、最壮观、规模最大、最有民族特色的王宫。


郑王庙(又称黎明寺)是泰国皇家寺庙之一,通称郑王寺。始建于大城王朝,当时名皇冠寺,后改称昌寺。郑王庙是纪念泰国第41代君王、民族英雄郑昭的寺庙。郑昭是华裔,于1768年,曾领导泰国各族人民奋驱外敌,重整江山,建立了吞武里王国。

郑王庙又名黎明寺,据说郑王驱逐缅甸军队后,顺湄南河经过此寺时正好是黎明时刻,便下令上岸到寺里参拜,后来登上王位后便称此寺为黎明寺。另一个说法是高79米的高塔每天最先接触到阳光,所以称之为黎明寺。

曼谷之前听说有恐怖袭击,但是去了之后,发现治安很好,甚至好过北京。物价又比北京上海便宜,特别是所有商品标签都除以5,感觉很爽。

Leave a Comment more...

Singapore春节游

by on 一.27, 2012, under Photography

趁着春节长假,抽空走马观花看了两个东盟邻国。
腊月二十八在首都机场登上午夜的航班,经过近7个小时的飞行,在早上9点到达新加坡。新加坡是一个汇聚了现代与传统的风格特色,融合东西文化之精粹。在这里,可以感受华人文化、马来文化及印度文化等多元民族特色。


鱼尾狮(The Merlion)是新加坡的旅游标志,是新加坡最高的自由式结构建筑。


莱佛士大酒店(Raffles Hotel)是新加坡地标性建筑,在1887年由来自于亚美尼亚的富豪薛克兹兄弟修建。从那时起,这座代表新加坡殖民地时期建筑风格的酒店便吸引了大量游客。毛姆、吉卜林、卓别林等人都曾在此下榻。1987年新加坡政府将这家酒店列为国家历史文物,并在1991年斥资1600万新币翻新。


新加坡和平纪念碑(Civilian War Memorial)位于美芝路纪念公园,象征新加坡四大种族在二战期间(1942年2月15日至1945年9月12日)共同挣扎求存的精神。同时也悼念在二次大战日军占领新加坡期间死难的平民。


圣安德烈教堂(St.Andrew’s Cathedral)位于哥里门街,1862年由隆纳德·麦克佛逊上校设计并兴建,用以代替早先为纪念史丹福·莱佛士爵士所建的旧教堂。


贯穿于整个城市的新加坡河是新加坡的生命之河,早期移民都是依靠这条河流来维持生计。漫步在河畔,您可以参观许多富纪念性的标志和建筑如鱼尾狮公园、莱佛士登岸遗址和旧国会大厦艺术之家,宗教机构如奥马清真寺、保赤宫,还有驳船码头、克拉码头等繁华的街区。


滨海艺术中心是新加坡首屈一指的艺术表演场地。造型独特的圆顶为它赢得了“榴莲”的称号。滨海艺术中心毗邻滨海湾,艺术中心内有音乐厅、剧院、演奏厅和排练室,还有户外表演空间。此外,这里还有购物中心、国际风味餐馆及新加坡首家艺术图书馆——滨海艺术中心图书馆,馆内收藏了大量关于音乐、舞蹈、电影和戏剧的书籍和多媒体资料。


滨海湾夜景


小印度是新加坡印度族群的聚集地,犹如印度的缩影。在屠妖节(Deepavali),即兴都教光节(HinduFestivalofLights),小印度被装点成金碧辉煌的神话世界。


甘榜格南是以生长在沼泽地周围的树木“gelam”树为名的,是新加坡穆斯林的聚居区。

新加坡真的很干净,一尘不染。天气跟北方的夏天一样热,每天30多度,但是一个苍蝇蚊子都没有。令人敬佩。

Leave a Comment more...

分布式文件系统MFS、Ceph、GlusterFS、Lustre的比较

by on 十二.29, 2011, under Linux

  MooseFS(MFS) Ceph GlusterFS Lustre
Metadata server 单个MDS。存在单点故障和瓶颈。 多个MDS,不存在单点故障和瓶颈。MDS可以扩展,不存在瓶颈。 无,不存在单点故障。靠运行在各个节点上的动态算法来代替MDS,不需同步元数据,无硬盘I/O瓶颈。 双MDS(互相备份)。MDS不可以扩展,存在瓶颈。
FUSE 支持 支持 支持 支持
访问接口 POSIX POSIX POSIX POSIX/MPI
文件分布/数据分布 文件被分片,数据块保存在不同的存储服务器上。 文件被分片,每个数据块是一个对象。对象保存在不同的存储服务器上。 Cluster Translators(GlusterFS集群存储的核心)包括AFR、DHT(和Stripe三种类型。

AFR相当于RAID1,每个文件都被复制到多个存储节点上。Stripe相当于RAID0,文件被分片,数据被条带化到各个存储节点上。

Translators可以组合,即AFR和stripe可以组成RAID10,实现高性能和高可用。

可以把大文件分片并以类似RAID0的方式分散存储在多个存储节点上。
冗余保护/副本 多副本 多副本 镜像
数据可靠性 由数据的多副本提供可靠性。 由数据的多副本提供可靠性。 由镜像提供可靠性。 由存储节点上的RAID1或RAID5/6提供可靠性。假如存储节点失效,则数据不可用。
备份       提供备份工具。支持远程备份。
故障恢复 手动恢复 当节点失效时,自动迁移数据、重新复制副本。 当节点、硬件、磁盘、网络发生故障时,系统会自动处理这些故障,管理员不需介入。
扩展性 增加存储服务器,可以提高容量和文件操作性能。但是由于不能增加MDS,因此元数据操作性能不能提高,是整个系统的瓶颈。 可以增加元数据服务器和存储节点。容量可扩展。文件操作性能可扩展。元数据操作性能可扩展。 容量可扩展。 可增加存储节点,提高容量可文件操作性能,但是由于不能增加MDS,因此元数据操作性能不能提高,是整个系统的瓶颈。
安装/部署 简单 简单 简单 复杂。而且Lustre严重依赖内核,需要重新编译内核。
开发语言 C C++ C C
适合场景 大量小文件读写 小文件 适合大文件。

对于小文件,无元数据服务设计解决了元数据的问题。但GlusterFS并没有在I/O方面作优化,在存储服务器底层文件系统上仍然是大量小文件,本地文件系统元数据访问是瓶颈,数据分布和并行性也无法充分发挥作用。因此,GlusterFS的小文件性能还存在很大优化空间。

大文件读写
产品级别 小型 中型 中型 重型
应用 国内较多 较多用户使用 HPC领域。
优缺点 实施简单,但是存在单点故障。 不稳定,目前还在实验阶段,不适合于生产环境。 无元数据服务器,堆栈式架构(基本功能模块可以进行堆栈式组合,实现强大功能)。具有线性横向扩展能力。

 

由于没有元数据服务器,因此增加了客户端的负载,占用相当的CPU和内存。

但遍历文件目录时,则实现较为复杂和低效,需要搜索所有的存储节点。因此不建议使用较深的路径。

很成熟、很庞大。

摘自存储实验室

Leave a Comment : more...

晨@北京

by on 十二.14, 2011, under Photography

清晨起来,眺望窗外……

Leave a Comment :, , more...

基于位置的实时游戏MapAttack的技术实现

by on 十.11, 2011, under NoSQL

MapAttack是一款实时的,基于地理位置的游戏,其通过Socket.io,node.js,和Redis实现了其实时性。下面文章对整个游戏及其技术实现进行了描述,推荐给大家。

这里我将向大家表述我们如何利用Socket.io、Redis、Node.js以及一路上我们所学的东西来规划、开发并测试一款实时的、基于位置的游戏。在过去的几个月中,我们将大部分的空闲时间都用在了开发一款实时游戏上,由于它是基于地图环境的,所以我们称之为“MapAttack!”,并把它作为我们LBS平台Geoloqi的一个测试程序。游戏中,两组队员互相竞争,尽可能多的攻占游戏界面上的小圆圈。而在这里,游戏界面其实就是城市中玩家周围的街道。

Geofence在这里指地图上带有数字的小圆圈,玩家进入这些小圆圈后,就会得到与圆圈中所标记数字相同的分数,它所在小组的总分数也会相应增加,同时圆圈的颜色会变成该玩家小组的颜色。

为什么要开发这么一款实时Geofencing游戏呢?

我们想要创造一款游戏,它能够让人们与真实世界物理交互,而不是像第一人称射击游戏或者即时战略游戏那样通过电脑控制台来交互。同时,我们也是受到了真人版吃豆人(PacManhattan)的启发。
技术挑战

处理用户进入区域的检测,同时留出200+的小圆圈。

处理一局游戏中所有手机位置信息的更新量(一局游戏有20或者更多用户)。
允许每台手机或者观看游戏的Web浏览器都能实时地看到玩家们的移动和圆圈颜色的改变。每台手机都会将它的位置发送给服务器,服务器会广播这些位置数据给其他手机以及观看游戏的浏览器。
处理GPS技术在不同智能手机模型间的错误和差异以保证游戏的公平体验。

GPS硬件的差异

众所周知,GPS信号反映了城市中高层建筑的情况。这就导致了在位置数据方面的不准确和不一致。这点在新手机上并不显著,但是在较老的手机上却十分明显。

MapAttack架构图

Socket.io

Socket.io是一个跨浏览器的Web套接字实现,它允许在浏览器上做实时数据更新,并且也支持老的浏览器。多亏了Socket.io我们可以利用最新的技术,同时不用要求所有我们的用户升级到最新的浏览器。这让我们实现了游戏中浏览器和手机间的即时更新。

Node.js

Node.js是谷歌浏览器的V8 Javascript引擎事件驱动的I/O实现,它由一个反应器实现,而这个反应器使得大量异步数据的传输得以实现。
当手机要发送数据时,我们用一个Node.js服务器将位置数据流从手机传输到Redis的发布频道或者订阅频道上。数据发布到Redis上,另一个Node服务器订阅该频道。我们的Node.js服务器接收手机通过一个类似Google’s Protocol Buffers的顾客协议发来的更新,实际上就是压缩的二进制的JSON。
当一个浏览器想要开始发送数据流时,它连接Socket.io服务器然后这个服务器订阅Redis的发布频道或订阅频道。这个Socket.io服务器通过Websockets向浏览器发送数据,如果Websockets不可用,闪回或者长轮询作为后备方式。
本质上讲,Socket.io允许我们使用Websockets规范,这是全新的,但同时也能工作在较老的浏览器上。

Redis

Redis是一个开源的、高级key-value存储系统,它支持消息队列使用发布/订阅模式。
从较高层次来说,Redis让我们所能做的事,是控制把数据实时发送到所有游戏中的手机和浏览器。游戏中的每一台手机把它的位置发送给服务器,服务器广播这些数据给其它手机和正在观看游戏的浏览器。
关于发布/订阅系统的一件吸引人的事:使用一个传统的系统你不得不维持许多连接,并且为了通过连接发送数据,你不得不重复发送,与发布/订阅系统不同,如果你有10,000个用户,你将不得不重复通过10,000个连接,发送回非常缓慢,并且容易出现死锁在套接字上的问题。

使用Redis的发布/订阅模式,就像启动一个广播电台。一旦把它打开了,人们(在这里指的是浏览器)就可以收听。这让我们能够把实时数据大规模地更新给客户端(浏览器和手机)。

Sinatra Synchrony

Sinatra::Synchrony是Sinatra的一个小扩展,它动态提升了Sinatra网络应用的并发性。由于EventMachine和EM-Synchrony的支持,当你有很多传输和低速IO请求时(如向外部APIs发送的HTTP请求),它增加了你的应用每个过程可服务的客户的数量。由于它内部使用Fibers处理堵塞的IO,所以没有回调的需要。这意味着我们可以像写一个正常的Sinatra网络应用那样进行开发。

Sinatra::Synchrony允许我们做异步程序,除了那些在Fibers中封装了回调操作的。这让我们能够实现同步程序的同时利用异步代码的优势。除了可以这样简单地变成,它也让我们根据需要可以转换一个不同的并发策略。

The MapAttack Game Server

最后,有一个MapAttack游戏的服务器,在这里,这个游戏服务器是一个简单的数据库,他负责存储玩家在地图上显示的所在点的数据,以及手机上玩家需要实时去抢夺的点的数据。

英文原文:blog.programmableweb.com
译文出处:www.lbsvision.com

Leave a Comment :, , , more...

大美长白

by on 十.08, 2011, under Photography

十一假期抽空来了趟短途游,去了中朝边境的长白山。

长白山,位于吉林省延边州安图县和白山市抚松县境内,是中朝两国的界山、中华十大名山之一、国家5A级风景区、关东第一山。因其主峰多白色浮石与积雪而得名,素有“千年积雪为年松,直上人间第一峰”的美誉。中国境内的白云峰海拔高度2691米,是东北第一高峰,而长白山最高峰是位于朝鲜境内的将军峰。长白山是中国东北境内海拔最高、喷口最大的火山体。长白山还有一个美好的寓意“长相守、到白头”。

10月1日从北京出发,10月5日返回。历程2600多公里,跨越河北,天津,辽宁,吉林。中朝边境的S303省道是这次旅程中风景最好的一段线路。

最美的风景都在路上

最美的风景貌似都在朝鲜那边,栅栏对面就是朝鲜人民主义共和国

天池边远眺

最美天池

S303省道边的白桦林

下面这条小河就是著名的鸭绿江,河对面就是朝鲜。想偷渡到朝鲜的,趟河过去就好了

随着往下游走,河面渐宽

Leave a Comment : more...


使用rvm在Mac中安装ruby和rails

by on 九.09, 2011, under Mac, Rails

MacOS默认安装的是ruby 1.8.7,如果你想使用ruby 1.9.2的话,除了在官网下载源码编译安装外,可以使用rvm来协助安装。

STEP-1 安装RVM

在Terminal中输入以下命令即可安装

1
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

为了可以在shell中使用,需要在.bash_profile中输入以下命令

1
2
3
4
5
cd ~/
sudo vim .bash_profile
 
#在.bash_profile中加入
[[ -s "$HOME/.rvm/scripts/rvm" ]] &amp;&amp; source "$HOME/.rvm/scripts/rvm"  # This loads RVM into a shell session.

之后退出Terminal,重启它。

STEP-2 安装Ruby

使用以下命令,可以看到rvm可以支持安装的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ rvm list known
# MRI Rubies
1.8.6[-p420]
1.8.6-head
1.8.7[-p352]
1.8.7-head
1.9.1-p378
1.9.1[-p431]
1.9.1-head
1.9.2-p180
1.9.2[-p290]
1.9.2-head
ruby-head
...

使用下面的命令安装ruby 1.9.2

1
rvm install 1.9.2

然后使用下面命令,让系统使用新的ruby

1
2
3
4
$rvm use 1.9.2
Using /Users/elton/.rvm/gems/ruby-1.9.2-p290
$ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.1.0]

当你重启机器后,你会发现又回复成了1.8.7了,可以使用下面命令,让系统默认使用1.9.2

1
rvm --default use 1.9.2

STEP-3 安装Rails

这步很简单

1
gem install rails

之后就可以使用最新的ruby和rails了。

3 Comments :, , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit my friends!

A few highly recommended friends...