Tony Huang 的个人资料Souline PLUS plan日志列表网络 工具 帮助

Souline PLUS plan

Anything + Souline = ?
4月16日

我看魔兽世界易主网易

错误太多了,我也懒的改了,这篇文章就留在这里当存档吧,大家不要看内容了啊~~

昨天消息就已经出来了,今天呢,消息已经确认了,这里发表一下我的看法吧。我想从三个角度看这个事情:

 

写那些名字挺麻烦的,以下所有的

    网易   我都用 NE 来代替

    九城   我都用 9C 来代替

    暴雪   我都用 AB来代替

    魔兽世界  我都用  WoW来代替

 

第一个角度:NE取得经营权

    NE取得经营权这一点得从两个方面来说:

    (1) NE的目的

        众所周知 WoW 是一款利润非常可观的网络游戏,这款游戏无论是从口碑、质量、活跃用户数量还是收入来说,都是非常优秀的。但是我们从9C和AB之间的合同(马上于5月份到期的那一份),和9C近年来的财报上可以看出,这样的合同对于国内的合作方来说已经是非常苛刻的了。从国内网游产业整体50%的利润率,而9C只有16%的利润率就可见一斑了。

        那么NE再以更加苛刻的条件拿到这个游戏的代理权的根本目的是什么呢?真的只是看好这款游戏的盈利能力嘛?从上面的分析,我们不难看出,没有那么简单。

        那么是什么导致了NE几乎是不惜代价地拿到了WoW的代理权的呢?我认为有以下几点:

        a. WoW作为一款精品网游,是NE产品线上所匮乏的高端产品。对于一个从门户网站转型过来的互联网公司来说,这对于网易的品牌价值的培养是非常有必要的。

        b. 现在国内的网络游戏市场主要有如下的几家大公司:盛大,具有完整的产品线,盈利能力、研发能力、已经取得经营权的进口网游大作也已经非常的多;9C,主要依赖WoW,其收入的90%来自WoW,但是近年来在自主研发上发力,2008年7月正式成立了TDC(The9 Development Center,九城研发中心),同时握有劲舞团2的代理权;NE,通过天龙八部游戏获得了几乎是超额的利润;巨人,以低端玩家为主;久游和腾讯,以休闲类游戏为主。那么和NE发生直接竞争的主要是盛大和9C,而其中的9C有时相对来说比较脆弱的,失去了WoW的9C将会非常脆弱,如果NE再继续做本文第三部分中所描述的事情,NE就会成长成为一个足以和盛大较量的网游公司。

    (2)NE的砝码

        在 天龙八部 的研发和运营上的巨大成功上来看,证明了NE有足够的实力运营好一款大型的MMORPG游戏,同时这也为NE提供了充足的现金,可以和AB进行谈判。这样NE就可以以更好的谈判条件,将代理权“夺”过来。

 

第二个角度:9C失去经营权

       9C失去代理权应该是说意料之外,情理之中的事情。公司的收入严重依赖于一款产品,一直以来是公司领导层的一块心病。9C也做了很多努力去改善这个状况,比如引进代理新的产品,自主研发产品,等等,但是结果只是杯水车薪。

       对于9C来说,他迟早是要面对这个问题的,只是时间问题。所以对于失去WoW的代理权,无论是对于9C还是对于大众来说,都是情理之中的事情,9C也不会感到特别的意外。说不定这反而能够成为一个公司进行转型的助推剂。现在陈晓薇要做的,我想最重要的是调整公司的战略,飞往美国,去安抚华尔街的情绪。

 

第三个角度:NE的后续步骤

        NE的后续步骤,在我看来最合理的做法是,等待9C的股价到一个相对低点,然后对9C发起收购。

 

唉,写不懂了,就先写这么多吧……

1月10日

AJAX 异步串行化库

今天总结了一下异步操作串行化的方法,写了个简单的串行化库
LIBRARY:  Souline.AsyncLoop.js
 
async = {
    // while
    wh : function(func_cond, func_body, func_end) {
        function _async_while() {
            if (!func_cond()) {
                func_end();
                return;
            }
           
            func_body(function() {
                // next
                _async_while();
            }, function() {
                // break
                func_end();
                return;
            });
        }
       
        _async_while();
    },
    //for
    fo : function(start, end, func_body, func_end) {
        var i = start;
        
        this.wh(function() { return i <= end; }, function(func_next, func_break) {
            func_body(i++, func_next, func_break);
        }, function() {
            func_end(i);
        });
    },
    // for each
    each : function(collection, func_body, func_end) {
        this.fo(0, collection.length - 1, function(i, callback) {
            func_body(collection[i], callback);
        }, func_end);
    }
};
 
用法也很简单啊:
async.fo(1, 40, function(i, func_next, func_break) {
    // do something on i
    if (breakCondition) {
        func_break();
        return;
    }
 
    func_next();
});
1月7日

AJAX 异步处理中的一种技巧

今天在写 九城 的 WebGame 九州战记的外挂(或者说插件吧),所有的操作都是异步的,有了一下的一点感悟:
 
在很多情况下,我们需要顺序的完成一些异步的操作:
比如:整理背包功能:
 
我们需要顺序的将每一个不在它应该在的位置上的物品移动到位置上,但是移动物品是异步的操作,这部分,我写了一个函数:
 
function moveCard(name, num, pos, newPos, callback) {
    $.get(moveCardUrl(name, num, pos, newPos), callback);
}
 
然后我写了个moveCards函数,遍历数组:
 
function moveCards(cards) {
    for(var i = 0; i < cards.length; i++) {
        var card = cards[i];
        var oldPos = card.Pos;
        var newPos = i + 1;
 
        if(oldPos != newPos) {
            moveCard(card.Name, card.Num, oldPos, newPos);
        }
    }
}
 
这是,所有的请求并不是串行执行的,(其实也不是完全并行的,因为HTTP协议规定,一个客户端和一个服务器之间最多能保持两个连接)
 
所以,我对这个函数进行了改变:
 
function moveCards(cards) {
    var i = 0;
 
    function _moveCard() {
        while((i < cards.length) && ((i + 1) == cards[i].Pos)) i++;
 
        if(i == cards.length)
            return;
 
        var card = cards[i];
        var oldPos = card.Pos;
        var newPos = i + 1;
 
        if(oldPos != newPos) {
            moveCard(card.Name, card.Num, oldPos, newPos, function() {
                i ++;
                _moveCard();
            });
        }
    }
}
12月21日

一些常用组件的用法

今天要用一些常用的组件,但是却忘记了具体的做法,在网上找了好久才,找到,就干脆在自己博客上记录一下,以后用起来方便一点吧。
 
1) Log4net 的配置和使用
配置:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <root>
  </root>
  <logger name="CallLog">
    <level value="ALL" />
    <appender-ref ref="CallLogAppender" />
  </logger>
  <appender name="CallLogAppender" type="log4net.Appender.FileAppender">
    <param name="File" value="E:\logs\World\CallLog.log" />
    <param name="AppendToFile" value="true" />
    <param name="RollingStyle" value="Date" />
    <param name="DatePattern" value="yyyy-MM-dd HH:mm:ss" />
    <param name="StaticLogFileName" value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%d [%t] %-5p %c - %m%n" />
    </layout>
  </appender>
</log4net>
具体的内容我也不解释,大家就将就着看吧,参考类的东西,就不用那么详尽了,呵呵
 
初始化:
        System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
        doc.Load(AppDomain.CurrentDomain.BaseDirectory + "log4net.config.xml");
        log4net.Config.XmlConfigurator.Configure(doc.DocumentElement);
使用:
        var log = log4net.LogManager.GetLogger("CallLogger");
        log.DebugFormat("{0}.{1} called", className, methodName);
 
2、Castle.Windsor.WindsorContainer
常用的轻量级IoC容器。
配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <components>
    <!-- WebServiceManager -->
    <component id="WebServiceManager"
               service="Souline.Basic.Facility.WebServiceManager, Souline.Basic.Facility"
               type="Souline.Basic.Facility.WebServiceManager, Souline.Basic.Facility">
      <parameters>
        <services>
          <item type="System.String">
            <item>Souline.World.WebService.LoginService, Souline.World.WebService</item>
          </item>
        </services>
      </parameters>
    </component>
   
    <!-- Logger -->
    <component id="CallLogger"
               service="Souline.Basic.Contract.ILogService, Souline.Basic.Contract"
               type="Souline.Basic.Facility.Log4netLogService, Souline.Basic.Facility">
      <parameters>
        <loggerName>CallLog</loggerName>
      </parameters>
    </component>
    <component id="CallLoggerInterceptor"
               type="Souline.Basic.Facility.Interceptor.LogServiceInterceptor, Souline.Basic.Facility">
      <parameters>
        <logService>${CallLogger}</logService>
        <logLevel>Debug</logLevel>
        <format>{datetime} [{pid}:{tid}] {class} : {method} > {message}</format>
      </parameters>
    </component>
   
    <!-- LoginService -->
    <component id="LoginServiceCache"
               service="Souline.Basic.Contract.ICacheService`2[[System.String], [System.Object]], Souline.Basic.Contract"
               type="Souline.Basic.Facility.LocalMemoryCacheService`2[[System.String], [System.Object]], Souline.Basic.Facility">
    </component>
    <component id="LoginService"
               service="Souline.Basic.Contract.ILoginService, Souline.Basic.Contract"
               type="Souline.Basic.LoginService, Souline.Basic">
      <parameters>
        <userCache>${LoginServiceCache}</userCache>
      </parameters>
      <interceptors>
        <interceptor>${CallLoggerInterceptor}</interceptor>
      </interceptors>
    </component>
              
  </components>
  <facilities>
    <facility id="factorysupport" type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />
  </facilities>
</configuration>
 
初始化:
        _container = new WindsorContainer(new Castle.Windsor.Configuration.Interpreters.XmlInterpreter(containerConfig));
使用:
        var loginService = _container.GetService<Souline.Basic.Contract.ILoginService>();
 
3、Castle.ActiveRecord
配置:
<?xml version="1.0" encoding="utf-8" ?>
<activerecord>
  <config>
    <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
    <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
    <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
    <add key="hibernate.connection.connection_string" value="Data Source=.;Initial Catalog=World;Integrated Security=True;" />
  </config>
</activerecord>
 
初始化:
        Castle.ActiveRecord.ActiveRecordStarter.Initialize(
            new Castle.ActiveRecord.Framework.Config.XmlConfigurationSource(
                AppDomain.CurrentDomain.BaseDirectory + "Castle.ActiveRecord.config.xml"),
            typeof(Souline.Basic.Entity.User));
实体类:
namespace Souline.Basic.Entity
{
    [ActiveRecord("Basic_Login_User")]
    public class User : ActiveRecordBase<User>
    {
        [PrimaryKey]
        public virtual int Id
        {
            get;
            set;
        }
        [Property]
        public virtual string Username
        {
            get;
            set;
        }
        [Property]
        public virtual string PasswordHash
        {
            get;
            set;
        }
        [Property]
        public virtual string Type
        {
            get;
            set;
        }
        [Property]
        public virtual string Status
        {
            get;
            set;
        }
        public static Entity.User FindByUsername(string username)
        {
            Entity.User[] users = FindAllByProperty("Username", username);
            if (users.Length == 0)
                return null;
            else
                return users[0];
        }
    }
}
用法:
    直接使用 Entity.User类就好了
 
12月19日

关于AOP和IoC

AOP是最近非常热门的词,全称叫作Aspect-oriented Programming,一直以来,我对这个名词的理解就是模模糊糊的。
 
知道最近在一个项目中,思考Log模块和Cache模块的设计的时候,才使我恍然大悟,原来AOP就是这么简单、自然的概念。
 
所谓的AOP就是希望程序员在编写程序的时候,能够完全关注在应用逻辑本身,而不用去考虑与逻辑本身无关的Facility的东西。我们拿那个经典的取钱的例子为例:
 
假设现在有一个
Account类,它保存的是用户帐户的余额:
 
Account

<<property>>Balance:decimal
Deposit(money:decimal)
Withdraw(money:decimal)

 

这时,程序员关注的流程是,Deposit和Withdraw本身,诸如校验余额啦,修改余额啦什么的。

这时项目经理过来跟架构师说,我们的客户是银行,需要将所有的调用记录都保存下来备案,保证数据的安全。 但是现在的架构如果需要添加这个功能,则必须在每个方法的开头、结束等处加上日志相关的代码,但这势必造成工作量快速变大。那么AOP是如何解决这个矛盾的呢?

那么让我们回头来思考这个问题:

我们分析一下项目经理的要求是:

所有操作,都要记录日志,能够反映程序中的热点代码、瓶颈和发生的错误

关键就是所有和操作两个词,让我们回想一下Dynamic Proxy的功能:

拦截某类的所有方法调用,并插入执行代码

是不是刚好和我们的需求有匹配的地方呢?

经过修改的Account类变成了这个样子:

Account

<<property>>Balance:decimal
<<virtual>>Deposit(money:decimal)
<<virtual>>Withdraw(money:decimal)

 

利用DynamicProxy类动态的生成一个继承自Account 类的类,并重写(override)它的所有方法,加入拦截的函数。再编写LogIntercepter类,记录所有的调用信息。

这就是AOP。我们没有修改原有的代码(基本没有,除了virtual关键字),但是却加入了某一方面(aspect)的功能。

 

那么我为什么要把AOP和IoC一起讲呢,因为他们之间有着密不可分的联系,我以.Net平台下的Castle库中的Windsor/MicroKernel容器为例。它提供了一个轻量级的IoC容器,提供了一种方便的方式配置组建和拦截器,基于配置的实现AOP的功能。具体的做法,我在下一篇文章中再写吧,有点晚了,去睡觉咯~

12月1日

一种有关锁的设计模式

今天看了一个ThreadSafeDictionary的代码,从里面看到了一个很好的使用锁的设计模式,首先从使用开始说:
 
public void Set(TKey key, TKey value)
{
    using(new WriterLock(this._lock))
    {
        // do something
    }
}
 
然后再看看WriterLock的实现:
class WriterLock : IDispose
{
    private ReaderWriterLockSlim _lock;
    public WriterLock(ReaderWriterLockSlim lock)
    {
        _lock = lock;
        _lock.GetWriterLock(lock);
    }
    public Dispose()
    {
        _lock.ReleaseWriterLock(_lock);
    }
}
 
他的作用机理是什么呢?
首先我们从using开始看,using语句中包含了一个new语句,这就调用了WriterLock中的ctor,也就是给目标加锁,而当需要保护的代码结束的时候,也就是using块之外的时候,这个新创建的对象已经不再生存期内,所以就自动调用了WriterLock中的Dispose方法。
 
这是非常值得借鉴的一种设计模式,在NHibernate和Castle.ActiveRecord中也有广泛的应用,比如:
using(new SessionScope())
{
    // do something
}
 
using(new Transaction())
{
    // do something
}
11月24日

SaaS与云计算

SaaS和云计算从精神上是想通的,但是他们提供了两个不同层面的服务。
 
SaaS将所有的硬件和软件细节都隐藏起来,提供一个软件服务的外表。
云计算将所有的硬件都隐藏起来,提供一个基础设施的外表。
 
他们有着共同的特点,就是
1)都隐藏了细节
2)都提供了服务
3)都需要强大的数据中心
4)都有良好的稳定性和容错能力(至少是宣称)
 
但是现在做SaaS的人的观点太狭隘了,把眼光都聚集在为企业提供信息化服务,我们为什么不能
为那些为公众提供服务的企业提供服务呢?
 
把论坛服务化?把CMS服务化?把SNS服务化?
 
这是一个崭新的市场,也是一个崭新的机会
 

Counter

This is Tony Huang's Counter
hit tracker

Friends

CSKSOFT