天天看點

我将系統從Windows遷移至Linux下的點點滴滴

一、寫在最前

  由于本人的技術水準有限,難免會出現錯誤。本文對任何一個人有幫助都是我莫大的榮幸,任何一個大神對我的點撥,我都會感激不盡。

二、技術選型

  在2013年8月低的時候,公司中了XXX市場監督局肉品配送車輛監控的項目。整個系統軟體部分需要實作的功能不難,最大的難點就是伺服器的系統要求是Linux的,其次就是10月底系統能夠初步成型。由于之前做的車輛監控系統都基于Windows的,要在短時間内完成這個項目,于是Mono就成了我的首選。張善友的部落格,也成了我經常光顧的地方,後來通過跟張哥的一番溝通。最終采用了張哥推薦的方案,資料庫使用PostgreSQL,Web伺服器使用國産的Jexus,Linux作業系統使用CentOS6.2(這個是客戶要求的)。其他都可以很好移植,之前的系統前台使用的Extjs+SilverLight+Asp.net,服務層使用的WCF,當時張哥有提醒我Mono下的WCF坑特别的多,ORM使用的是Nhibernate,地圖引擎依舊采用的DeepEarth。有張哥的點撥,我的信心滿滿的。看上去似乎我需要解決的問題就是搭建好Mono環境,搞定Mono下的WCF服務就可以大功告成了。

三、環境搭建

  裝Linux系統,配置Mono環境,安裝Jexus。寫了個簡單的Silverlight + WCF的程式部署到Jexus,竟然成功跑來了,當時的我偷笑了。然而,當我真正把系統部署到Jexus上面,才知道噩夢才剛剛開始。登入頁面都沒有過不去,此時估計很多人都會想要是在Linux下能Debug就好了,我也不例外。于是乎,我就折騰MonoDevelop,安裝過程中遇到各種奇葩問題,而且不支援SilverLight的項目,我放棄了。采用最原始的辦法,寫日志。經過幾天的折騰,終于可以跑起來了。

四、WCF攻克

  由于在折騰WCF的時候,遇到的默默其妙的問題比較多,有些能夠通過Google搜尋到。我這裡就列舉三個。

  1:尋址版本 AddressingNone不支援添加 WS-Addressing 标頭。如果你的WCF部署在IIS下能正常的調用,而部署在Jexus下确抛出這種異常。

  檢查項目下引用的dll,估計某些dll在Mono下支援不是很好。在有可能出錯的異常寫日志,而不要抛出這個異常。比如下面這個簡單的例子,FluentNhibernate在Mono下的支援就不是很好。

public IList<Custmer> GetCustoemrs()
        {
            try
            {
                using (var session = SessionFactory.GetCurrentFactory().OpenSession())
                {
                    var query = session.CreateQuery("from Customer ");
                    return query.List<Customer>();
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteException("GetCustomers method raise error:", ex);
                //throw;
            }
        }

        public class SessionFactory
        {
            public static ISessionFactory GetCurrentFactory()
            {
                return sessionFactory ?? (sessionFactory = CreateSessionFactory());
            }

            private static ISessionFactory CreateSessionFactory()
            {
                return Fluently.Configure()
                      .Database(MsSqlConfiguration.MsSql2008.ConnectionString(
                                     x => x.FromConnectionStringWithKey("db")))
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerMap>())
                    .BuildSessionFactory();
            }

            private static ISessionFactory sessionFactory
            {
                get;
                set;
            }
        }      

  2:用戶端反序列化失敗

  當我把WCF部署到Jexus,成功生成本地代理後,調用WCF就會一直報這個錯誤。究其原因,就是因為生成代理類的屬性指定了反序列化的順序,而服務端我是沒有指定的。是以,先把WCF服務部署到IIS下,然後用戶端位址指向部署IIS的WCF服務,重新更新服務,再把配置檔案中位址指向部署在Jexus的WCF服務即可。

Jexus下Host的WCF服務生成的本地代理:

[System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=1)]
        public string CustomerCode {
            get {
                return this.CustomerCodeField;
            }
            set {
                if ((object.ReferenceEquals(this.CustomerCodeField, value) != true)) {
                    this.CustomerCodeField = value;
                    this.RaisePropertyChanged("CustomerCode");
                }
            }
        }      

IIS下Host的WCF服務生成的本地代理:

[System.Runtime.Serialization.DataMemberAttribute()]
        public string CustomerCode {
            get {
                return this.CustomerCodeField;
            }
            set {
                if ((object.ReferenceEquals(this.CustomerCodeField, value) != true)) {
                    this.CustomerCodeField = value;
                    this.RaisePropertyChanged("CustomerCode");
                }
            }
        }      

  3:WCF服務導緻Jexus的httpd worker不斷重新開機

  這個問題是困擾我最久的,在IE11下最為明顯,Service.svc檔案的post請求,動不動就被Pending。在最開始我不知道Jexus一直在重新開機,因為我看jws.log中沒有Jexus重新開機的日志。後來得知可以通過 ps –ef | grep jws 指令可以檢視程序的啟動時間,如下圖所示,可以看到httpd worker程序不斷的重新開機。

我将系統從Windows遷移至Linux下的點點滴滴

  問題找到了,又開始新的一輪折騰。首先,減少用戶端對Service.svc檔案的post請求,其次将WCF回傳的資料進行壓縮處理。一番修改後,IE11下,故障依舊。于是想到了用WebService嘗試下,就把某個服務修改成WebService的方式後,貌似httpd worker不會重新開機了,處理不過來請求的時候,隻會中斷請求。既然這樣,我就狠心把WCF的綁定方式由CustomBinding的方式改成了BasicHttpBinding,然而故障還是那麼的頑固存在。

  經過一番測試,感覺可能是Jexus的問題,測試代碼:

class Program
    {
        private static ServiceClient client;
        private static int count = 0;
        static void Main(string[] args)
        {
            client = new ServiceClient();
            client.GetCustomersCompleted += client_GetCustomersCompleted;
            int callCount = Convert.ToInt32(ConfigurationManager.AppSettings["CallCount"]);
            Console.WriteLine("Press begin to Call WCF Service:");
            while (Console.ReadLine().ToLower() == "begin")
            {
                DoWork(callCount);
            }
            Console.Read();
        }
        private static void DoWork(int callCount)
        {
            for (int i = 1; i <= callCount; i++)
            {
                ThreadPool.QueueUserWorkItem(CallService, i);
            }
        }
        private static void CallService(object state)
        {
            client.GetCustomersAsync(state);
        }
        static void client_GetCustomersCompleted(object sender, GetCustomersCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Interlocked.Add(ref count, 1);
                Console.WriteLine("成功調用:{0}", count);
            }
            else
            {
                Console.WriteLine(e.Error);
            }
        }
    }
}      

  如果是對WebService模拟并發發起請求的時候,httpd worker不會重新開機,如果是WCF,httpd worker會不斷的重新開機。沒有辦法隻有咨詢Jexus的作者宇内了。跟他描述了問題,按照他說的更新了Jexus版本,優化了Linux。最後發了個測試工具給宇内,宇内發現Jexus在處理WCF請求的時候是有點問題。在這裡還是要感謝宇内那麼熱心的幫助我解決問題。

五、資料庫遷移

     資料庫的遷移遇到的問題不是很多,借助navicat将資料從SqlServer導入到PostgreSQL。由于系統的業務不是很複雜,之前就采用了Nhibernate,需要修改配置檔案中主鍵字段的映射,因為PostgreSQL中采用的是序列。之前SqlServer中一些稍微複雜點的查詢是采用存儲過程寫的,存儲過程的移植性不好,在PostgresSQL下這部分隻能重新寫了。

<id name="TypeID" type="Int32" unsaved-value="0">
           <column name="TypeID" length="4" sql-type="int" not-null="true" unique="true" index="PK_SysAllType"/>
      <generator class="sequence">
        <param name="sequence">sysalltype_typeid_seq</param>
      </generator>
</id>      

六、地圖遷移

  由于地圖伺服器是單獨一台,最終是用Perl + Apache來實作的,實作也挺簡單的,幾十行代碼,就是地圖引擎請求一個圖檔路徑,将圖檔輸出就可以了。最開始的時候,用的是Asp.net,部署在Jexus上,但是不知道為什麼某些圖檔始終輸出不出來,最終還是放棄了。

最後附上一張系統遷移後成功後的圖檔:

我将系統從Windows遷移至Linux下的點點滴滴