第一次创业回想录:从博客走向果壳网那荡起与陨落的三年(2011-2013)

前言:

吃完饭,一躺下,一个梦,醒了……

即时快要踏上第二次创业的道路了,详情见:一个想方设法(续三):一份IT技术联盟创业计划书,开启众筹创业征程

既然如此今夜无眠寂静,就静静回想下当年第一次创业的时光吧。

在自家博客的十年里,那几年基本淡出博客园,离开了社区。

故很两人不知道我那段时光,我也从不记录过程,前些天就与大伙分享一篇回想录吧。

前言

明天我们还在热议关于.NET Core的事物,我只想说一句:在.NET
跨平台叫了如此多年间,其实人们盼望的是一个知名的跨平台案例,而不是一堆能跨平台的音信。

好,回头说说框架: 

在框架形成数据库读写分离的功用后,起头回想起2年前所构思的:关于框架集成分布式缓存MemCached的实现。

事先平素没动手,是因为思路比较飘,秉承着框架应该简单干净轻量引用无依靠和应用不复杂的思索:

看着MemCached的服务端协议,整天思考着自己用Socket写一个客户端。

后来说不定是没心境,迟迟没出手。

又在某个时刻,想过偷懒,想动态反射Memcached.ClientLibrary库,但一看到它仍旧关联了六个dll,我这纯洁的心就有点抗拒它了。

就此日子就这样飘来复去,这个效用被想起又遗忘……

二〇一〇年,这年,我还很年轻

在成功SilverLight+WCF网络象棋的层层发布后,开始会合了某些人气。

未来便起先重新(往日开展过一遍,但功亏一篑了)进行了CYQ.Data开源系列的宣布,

趁着框架类其它散播,与人气的会聚,便不停的有网友提出我用框架赚钱(提议包括与塑造机构创立协作,与商家的迅猛支付建立协作等办法)

在网友的洗脑中,我依旧迷糊的行动了。

当时本人找了一个高校的同乡师弟,试图说服他投钱(他这时候相比较有钱)。

而我登时只明白要把框架形成普及,但怎么赚钱怎么先导之类的,都不曾想过。

她叫上了另一个搞技术的爱人和我一块儿吃了下饭聊这事,但结果并不太被看好。

莫不按照同乡与师兄关系,最后说可以先给自己2万块让自己去搞市场调研先。

新兴就从不新生了。。。

 

框架集成MemCache

这几天,翻看了过去下载的的相干源码中,有个叫BeITMemcached,扫了一下源码,发现简单轻量没外部引用。

于是就起来改造,并测试了一下,没发现问题,于是从头考虑集成的章程。

框架的缓存类本来很孤独,唯有一个:CacheManage.cs

现行改建完,多了10个类,编绎后dll大小多了30K,说多了也是泪~:

图片 1

2011开春,这年,我也还很年轻

乘机CYQ.Data的开源类别的版本升级与增温,秋色园QBlog体系也大肆的展开了。

黑马的某一天,有位网友让自家帮她写了一个概括的基本点字站。

她也起始教我咋做紧要字站赚钱,给了我一批传奇的显要字。

自己用一个域名试点,一个多星期后,大概一天有几十到一百五个IP。

他牵线人收了自己这点流量,给了几十块,说要做大,至少要投入几千个域名,N台服务器,搞N三个IP。

接下来自己就没跟进了,毕竟传奇关键字已经重重人在做了,他也在做。

新兴的某一天,有人倾心QBlog中的CMS性质,说能够做站群,起初洗脑我出去创业。

在她的发动下,我创了一个知乎IT创业交流群,里面插足了20多私家,聚在联名座谈。

并且形成了一份简单的不预计划的计划,这里仍然有存在:http://www.cyqdata.com/cy.html

可是只有研商,大伙并从未行动,每个人都向自家表明了她们难以行动的说辞。

部分还特意向自己写了一份文档,提议一份指出,表示仅会在机会恰到好处的时候才会进入。

 

框架的代码重构思维

2011年中,那年,我荡着年轻,辞职出来创业了

霎时,集团两年的合同期到了,我和领导者说就不再续了,我要出来创业了。

旋即的状态,除了被网友洗脑,DZ论坛的振奋,还有一腔热血。

由于QBlog要走商业化,所以,做为组件之一的CYQ.Data也非得闭源了,故本来发展事势可以的开源组件,也为此走上闭源商业化的天地里。

青春是荡漾着,但对于路该怎么走,其实自己很不解,没有工作,一个人。

每日沉浸在改代码,加效果,发表新本子中,以持续的无暇景观来麻木自己的缅怀。

下一场早晨写技术博文,教程,或录视频,等到下午公布于博客之中。

每一天靠着网友的支撑和赞在锲而不舍着不明了该怎么坚定不移的百折不回不懈。

就这样在寂寞中不停了一点个月。。。

 

终极定案的重构思维:

1:原有的CacheManage类变更为LocalCache。

2:CacheManage类变更为抽象类

3:新增MemCache,并和LocalCache一并落实CacheManage抽象类方法。

以上三步,就是基本的盘算。

二零一一年终,这年,QBlog无望,果壳网初现

在这段忙碌孤独的时光里,每一天百折不挠分享的的技艺著作。

并从未我所预想中的迎来使用的对象用户,而是迎来了一群要源码的伸手党。

其后就慢慢走向了开源的路途……

开源后,离商业更是摇摇无期了,中间偶尔有分别感兴趣的问价钱,我自己都不知情该怎么应答了……

……

有一天,我如故是呆在房间里,不知晓是改着功效,或是录着视频,依然写着博文。

对象给本人打了个电话,说天猫上有很多卖博客园粉丝的,1000粉4块钱,问我能无法写个软件刷粉丝。

还发了有的此外刷粉软件的以身作则截图,说可以的话,软件写出来后,他帮我卖软件。

觉的左右现状也就这样了,就花了几天把软件写好给他了(一发端功用就是导入一批账号,然后点击批量关心一个号)

发过去从此,基本也没啥进展,因为她要先购买一批账号,而这批账号,好多都是丰富账号,所以赚不到钱。

……

从此,偶尔没事也帮另一个爱人写了一个自行投票机(首即使自行断开重拔+简单的图片验证码自动识别)

……

在飘渺中的我,莫名的就顺手把原先的刷粉软件改进了一晃,把它从单机变成了多用户+平台式的互粉形式。

后来,走上了果壳网创业的方向……

一初叶的重构思维:

1:从原始的CacheManage里提取接口ICache

2:CacheManage改名WebCache并继承自ICache(由于提口提取自本类,所以代码不需要怎么调整)

3:新建MemCache继承并贯彻ICache接口。

4:新建CacheManage类,并从WebCache处把Instance实例属性移到此类中,并赶回对应的ICache接口。

2012-2013年,随今日头条荡起陨落的2年

从今创业的可行性切入到网易,我大旨脱离了果壳网了,淡出技术社区了。

自从宣布了和讯知乎的互粉平台软件,用户增添很快,市场需求很大。

我拉上了一个情侣,中间在网上征集了十多少个全职的学童党联合搞这事。

以下分享中间历经的诸多折腾事:

编绎后发现:

因为本来的代码有一小部分是这样写的:CacheManage
cache=CacheManage.Instance;

因为重回的品种不一致,原有代码受到震慑,必须改成:ICache
cache=CacheManage.Instance。

为了逃避影响不用改动代码,所以最后改用定案里抽象类和架空方法实现。

1:官网果壳网被封

在官网天涯论坛账号注册后,不到一礼拜,真实用户的粉丝就跨越1w+了,每日的数字都在提升。

正兴奋于才刚始的好征兆,官网账号就被知乎网易给封了,申诉也没用。

于是后来就从未有过官网天涯论坛了,但建了官网网站。

下边贴一下抽象类CacheManage的办法:

/// <summary>
    /// 全局缓存类
    /// </summary>
    /// <example><code>
    /// 使用示例:
    /// 实例化: CacheManage cache=CacheManage.Instance;
    /// 添加:   cache.Add("路过秋天",new MDataTable);
    /// 判断:   if(cache.Contains("路过秋天"))
    ///          {
    /// 获取:       MDataTable table=cache.Get("路过秋天") as MDataTable;
    ///          }
    /// </code></example>
    public abstract class CacheManage
    {
        #region 对外实例
        /// <summary>
        /// 返回唯一实例(根据是否配置AppConfig.Cache.MemCacheServers的服务器决定启用本地缓存或分布式缓存)
        /// </summary>
        public static CacheManage Instance
        {
            get
            {
                if (string.IsNullOrEmpty(AppConfig.Cache.MemCacheServers))
                {
                    return LocalShell.instance;
                }
                else
                { 
                    return MemShell.instance;
                }
            }
        }
        /// <summary>
        /// 单机本地缓存
        /// </summary>
        public static CacheManage LocalInstance
        {
            get
            {

                return LocalShell.instance;
            }
        }

        class LocalShell
        {
            internal static readonly LocalCache instance = new LocalCache();
        }
        class MemShell
        {
            internal static readonly MemCache instance = new MemCache();
        }
        #endregion
        /// <summary>
        /// 添加一个Cache对象
        /// </summary>
        public abstract void Add(string key, object value);
        public abstract void Add(string key, object value, double cacheMinutes);
        public abstract void Add(string key, object value, string fileName);
        public abstract void Add(string key, object value, string fileName, double cacheMinutes);
        public abstract void Add(string key, object value, string fileName, double cacheMinutes, CacheItemPriority level);
        public abstract Dictionary<string, CacheDependencyInfo> CacheInfo { get; }
        public abstract MDataTable CacheTable { get; }
        /// <summary>
        /// 清除所有缓存
        /// </summary>
        public abstract void Clear();
        public abstract bool Contains(string key);
        /// <summary>
        /// 获和缓存总数
        /// </summary>
        public abstract int Count { get; }
        /// <summary>
        /// 获得一个Cache对象
        /// </summary>
        public abstract object Get(string key);
        /// <summary>
        /// 获得一个Cache对象
        /// </summary>
        public T Get<T>(string key)
        {
            object o = Get(key);
            if (o != null)
            {
                Type t = typeof(T);
                try
                {
                    return (T)StaticTool.ChangeType(o, t);
                }
                catch (Exception err)
                {
                    Log.WriteLogToTxt(err);
                    return default(T);
                }
            }
            return default(T);
        }
        /// <summary>
        /// 获取目标的文件依赖是否发生更改
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public abstract bool GetFileDependencyHasChanged(string key);
        /// <summary>
        /// 获取缓存对象是否被手工标识为已更改
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public abstract bool GetHasChanged(string key);
        /// <summary>
        /// 还可用的缓存字节数
        /// </summary>
        public abstract long RemainMemoryBytes { get; }
        /// <summary>
        /// 还可用的缓存百分比
        /// </summary>
        public abstract long RemainMemoryPercentage { get; }
        /// <summary>
        /// 删除一个Cache对象
        /// </summary>
        public abstract void Remove(string key);
        /// <summary>
        /// 缓存设置:有则更新,无则添加
        /// </summary>
        public abstract void Set(string key, object value);
        public abstract void Set(string key, object value, double cacheMinutes);
        /// <summary>
        /// 手动对缓存象标识为已更改
        /// </summary>
        public abstract void SetChange(string key, bool isChange);
        /// <summary>
        /// 更新缓存,缓存存在则更更新,不存在则跳过
        /// </summary>
        public abstract void Update(string key, object value);
        public abstract string WorkInfo { get; }

    }

此间新增对外一个性能:LocalInstance,是因为一但配备了AppConfig.Cache.MemCacheServers后:

原本的本机缓存就活动切换来分布式缓存,为了采纳本机缓存依然可以选拔,所以提供LocalInstance属性。

一开首是对外多少个:Instance(自动切换型)、LocalInstance、MemInstance。

我们可以考虑一下,为何MemInstance被去掉了?感觉有点说不清道不明的感觉到。

 

是因为LocalCache是改变名称自CacheManage,而CacheManage在原先随笔贴过源码,所以不另行了。

2:登陆入口被封

由于互粉属于棕色区,不可能光明正大的走接口,技术上都是分析http协议,模拟进行。

官网搜狐刚被封不久,软件的新浪登陆也被封杀了(当时是分析的知乎网首页登陆,再跳转乐乎的)。

于是寻找了虎扑网的其它登陆入口(毕竟新浪的频段很多,登陆其中一个,都可以跳到博客园)。

没过几星期,然后又被封杀了。

万般无奈,引入了WebBrowser组件,登陆时弹出浏览器窗口让用户手工登陆。

网易虽封杀不了,但用户体验差了不少过多。

现今贴一下MemCache的源码:

  1  /// <summary>
  2     /// 分布式缓存类
  3     /// </summary>
  4     internal class MemCache : CacheManage
  5     {
  6         MemcachedClient client;
  7         internal MemCache()
  8         {
  9             MemcachedClient.Setup("MyCache", AppConfig.Cache.MemCacheServers.Split(','));
 10             client = MemcachedClient.GetInstance("MyCache");
 11 
 12         }
 13 
 14         public override void Add(string key, object value, double cacheMinutes)
 15         {
 16             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
 17         }
 18         public override void Add(string key, object value, string fileName, double cacheMinutes)
 19         {
 20             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
 21         }
 22 
 23         public override void Add(string key, object value, string fileName)
 24         {
 25             client.Add(key, value);
 26         }
 27 
 28         public override void Add(string key, object value)
 29         {
 30             client.Add(key, value);
 31         }
 32 
 33         public override void Add(string key, object value, string fileName, double cacheMinutes, CacheItemPriority level)
 34         {
 35             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
 36         }
 37 
 38         public override Dictionary<string, CacheDependencyInfo> CacheInfo
 39         {
 40             get { return null; }
 41         }
 42         DateTime allowCacheTableTime = DateTime.Now;
 43         private MDataTable cacheTable = null;
 44         public override MDataTable CacheTable
 45         {
 46             get
 47             {
 48                 if (cacheTable == null || DateTime.Now > allowCacheTableTime)
 49                 {
 50                     cacheTable = null;
 51                     cacheTable = new MDataTable();
 52                     Dictionary<string, Dictionary<string, string>> status = client.Stats();
 53                     if (status != null)
 54                     {
 55 
 56                         foreach (KeyValuePair<string, Dictionary<string, string>> item in status)
 57                         {
 58                             if (item.Value.Count > 0)
 59                             {
 60                                 MDataTable dt = MDataTable.CreateFrom(item.Value);
 61                                 if (cacheTable.Columns.Count == 0)//第一次
 62                                 {
 63                                     cacheTable = dt;
 64                                 }
 65                                 else
 66                                 {
 67                                     cacheTable.JoinOnName = "Key";
 68                                     cacheTable = cacheTable.Join(dt, "Value");
 69                                 }
 70                                 cacheTable.Columns["Value"].ColumnName = item.Key;
 71                             }
 72                         }
 73                     }
 74                     cacheTable.TableName = "MemCache";
 75                     allowCacheTableTime = DateTime.Now.AddMinutes(1);
 76                 }
 77                 return cacheTable;
 78             }
 79         }
 80 
 81         public override void Clear()
 82         {
 83             client.FlushAll();
 84         }
 85 
 86         public override bool Contains(string key)
 87         {
 88             return Get(key) != null;
 89         }
 90 
 91         //int count = -1;
 92         //DateTime allowGetCountTime = DateTime.Now;
 93         public override int Count
 94         {
 95             get
 96             {
 97                 int count = 0;
 98                 MDataRow row = CacheTable.FindRow("Key='curr_items'");
 99                 if (row != null)
100                 {
101                     for (int i = 1; i < row.Columns.Count; i++)
102                     {
103                         count += int.Parse(row[i].strValue);
104                     }
105                 }
106                 return count;
107             }
108         }
109 
110         public override object Get(string key)
111         {
112             return client.Get(key);
113         }
114 
115 
116         public override bool GetFileDependencyHasChanged(string key)
117         {
118             return false;
119         }
120 
121         public override bool GetHasChanged(string key)
122         {
123             return false;
124         }
125 
126         public override long RemainMemoryBytes
127         {
128             get { return 0; }
129         }
130 
131         public override long RemainMemoryPercentage
132         {
133             get { return 0; }
134         }
135 
136         public override void Remove(string key)
137         {
138             client.Delete(key);
139         }
140 
141         public override void Set(string key, object value)
142         {
143             client.Set(key, value);
144         }
145 
146         public override void Set(string key, object value, double cacheMinutes)
147         {
148             client.Set(key, value, DateTime.Now.AddMinutes(cacheMinutes));
149         }
150 
151         public override void SetChange(string key, bool isChange)
152         {
153 
154         }
155 
156         public override void Update(string key, object value)
157         {
158             client.Replace(key, value);
159         }
160 
161         DateTime allowGetWorkInfoTime = DateTime.Now;
162         string workInfo = string.Empty;
163         public override string WorkInfo
164         {
165             get
166             {
167                 if (workInfo == string.Empty || DateTime.Now > allowGetWorkInfoTime)
168                 {
169                     workInfo = null;
170                     Dictionary<string, Dictionary<string, string>> status = client.Status();
171                     if (status != null)
172                     {
173                         JsonHelper js = new JsonHelper(false, false);
174                         js.Add("OKServerCount", client.okServer.ToString());
175                         js.Add("DeadServerCount", client.errorServer.ToString());
176                         foreach (KeyValuePair<string, Dictionary<string, string>> item in status)
177                         {
178                             js.Add(item.Key, JsonHelper.ToJson(item.Value));
179                         }
180                         js.AddBr();
181                         workInfo = js.ToString();
182                     }
183                     allowGetWorkInfoTime = DateTime.Now.AddMinutes(5);
184                 }
185                 return workInfo;
186             }
187         }
188     }

讲完实现的经过和贴完源码,下边讲一下行使过程了:

3:360等杀毒软件报病毒

出于软件需要加密的急需,360都会报病毒,每趟和360讨价还价,都是提交exe文件过去,然后等。

但是由于软件更新的反复很快,很多时候都是子夜更新提高,360的人工,也只是唯有你催了许多次都理你三回。

如此上下被坑了半年多,直到自己揭橥了另一个知乎互评软件,忍无可忍用用大方账号去360精兵周鸿祎的果壳网下评论刺激时。

及时才有新的工作人士跟进,并给了自身一个自助提交的VIP账号,才算干净的解决360报病的题材了。

但还有QQ的杀毒卫士之类的,反正够折腾,但也没办法。

光杀毒软件的下载病毒指示,就损失了无数用户。

框架里拔取MemCache效能的演示

4:过于单纯天真的合计

即使有各样限制,但也阻止不了大量的急需和用户。

迅猛,知乎粉丝精灵已经改为市面上该类软件的首先。

先前时期推出的果壳网的中号互评、互转软件,也变为很受欢迎的效果。

乘势和讯营销概念的燥热,知乎有奖转发活动也无处荡开。

有成千上万的用户找上自家谈合作,利用阳台大号举办有奖活动转发。

也有过多其他的想法:制作很多的头条工作等。

……

当真,大量的中号,当水军涌向哪哪就得死五遍。

在应当快速捞一把的门类:我却坚称着免费,想着靠免费统一江湖。

在黑色的行当里:我却想着正义不添乱,盼着软件能走上美好。

惟有天真的无可救药。

 

1:服务端先安装,并运行起来

服务端的公文是这般的:

图片 2

运转后的劳动是这么的,这里开了七个服务过程,分别对应:11211和11212端口:

图片 3

4:实名制限制

趁着国家对实名制的要求,软件上提供的飞跃大号注册效率已经不可能用了。

于是乎大号的立即减掉,带来的结果就是平均每能刷的粉丝大量的缩减了。

事先平均一天能刷几千的,到最终只可以刷到几百了。

 

2:代码应用是这般的

图片 4

原始的应用办法不变,只是扩展了一行配置,就活动切换来分布式了,是不是从单机过渡到分布式太简单了。

普普通通大家不在代码里安排,而是安排在:

图片 5

运行的结果是这般的:

图片 6

5:没落的网易微博

乘机实名制与僵尸粉的打击力度加强,让水更是清时,浑水摸鱼的今日头条营销概念也开头退潮了。

给予微信和活动互联网的勃兴,天涯论坛和讯已日趋萎缩。

 

总结

应用此框架,不管是进化到数据库读写分离,如故演进到分布式缓存,整个架构的升级历程,只需追加1行布局文件。

几年前就径直在构思,浮浮沉沉地就势框架的演进,近来顺水推舟地促成了,想想都认为有点不堪设想。

除此以外最终Top150大神群里,有人问我,最近写的著作有人打赏么?我只弱弱的回了一句:还没。

6:转向移动互联网的风投

在经历各样坑之后,克制各类困难后,

带着市面上同类软件第一的名头,带着几十万的用户数据,在知乎网易上联系了两个投资人。

和率先个投资人在尼科西亚约见了面,还被带去参观加一个基于知乎知乎的创业团队,他们是做多少解析的。

对于大家的体系,他们只有30万的投资意向,所以也就不停了之。

第二个投资人,用知乎私信联系后,直接打电话的,直接就被报告已经不投博客园今日头条的品种了。

 

7:无力回天的自力更生

当投资的风向标已转,果壳网也起始式微,该何去何从?

唯其如此思考各类怎么着赚钱生存的不二法门:

1:对外提供刷粉、转发、评论工作。

(在软件一最先,没有采集中号,是一大失误,导致失去大量的账号,在实名制后,中号的发生已经很艰辛)

2:提供公司账号代运营。

3:软件增添VIP(扩大魔法值(积分)购买)

4:软件扩大一个大的广告轮播(广告位出租)

低收入从几百到几千再回归到几百,即便有几十万的用户,大环境天涯论坛已没落,依附于和讯的也全都无力回天了。

……

……

……

总结:

道理讲出一二三,也入不了大伙的心。

稍稍事,需要自己经历,才刻骨于心。

但经历五遍创业便可教会人不少居多。

创业败北率高的原故相似的在于总在于:思维太年轻,智商不够用,经验不足。

于是每经历三遍破产,多三次合计总结,就累积了多一份成功的概率。

一回完整的创业,最好有:革新的觉察,谨慎的计划,坚决的行引力,看透成败的情感。

发表评论

电子邮件地址不会被公开。 必填项已用*标注