彩云项目总结

创建服务

创建一个类继承HAComponent

因为服务都是受HA管理的,我们通常会用HAConsole发消息给HACenter,HACenter管理多个HAWorker,创建的这个类,就是一个worker。

初始化代码

 

ServiceSettings.InitService(serverName);

SipcStack.Initialize();--协议栈

 

ServiceHelper.Initialize();

RouteDirector.Initialize();--路由

 

最大连接数设置

ServicePointManager.DefaultConnectionLimit= IDCSConfig.Current.DefaultConnectionLimit;

ServicePointManager.SetTcpKeepAlive(true,IDCSConfig.Current.KeepLiveTime,

IDCSConfig.Current.IntervalTime);

 

////注册RPC通道,server,其他服务调用自己会用到

  RpcSipcServerChannel _sipChannel = newRpcSipcServerChannel(IPAddress.Parse("0.0.0.0"),ServiceSettings.Current.RpcServerPort);

               RpcServiceManager.RegisterServerChannel(_sipChannel);

////注册client通道,调用其他服务会用到

RpcProxyFactory.RegisterClientChannel(newRpcSipcClientChannel());

 

 

 

                //RpcHttpServerChannel_httpChannel = newRpcHttpServerChannel(ServiceSettings.Current.HttpServerPort);

               //RpcServiceManager.RegisterServerChannel(_httpChannel);

 

               RpcServiceManager.RegisterService<IIDCService>(new IDCService());

                RpcServiceManager.Start();

 

配置

先写一个类,对应HA里面的配置节。例如这个类:

 [IICConfigSection("IDCService")]

   public class IDCSConfig : IICConfigSection

   {

        private static object _syncObject = newobject();

        private static IDCSConfig _current;

 

        public static IDCSConfig Current

        {

            get

            {

                if (_current == null)

                {

                    lock (_syncObject)

                    {

                        if (_current == null)

                        {

                            _current =IICConfigurationManager.Configurator.GetConfigSecion<IDCSConfig>(

                                "IDCService",

                               delegate(IDCSConfig settings)

                                {

                                    _current =settings;

                                }

                            );

                        }

                    }

                }

 

                return _current;

            }

        }

 

        [IICConfigField("ClientId",DefaultValue = 0)]

        public int ClientId;

 

       [IICConfigField("DefaultConnectionLimit", DefaultValue = 500)]

        public int DefaultConnectionLimit;

 

       [IICConfigField("KeepLiveTime", DefaultValue = 30000000)]

        public int KeepLiveTime;

 

       [IICConfigField("IntervalTime", DefaultValue = 2000000)]

        public int IntervalTime;

 

       [IICConfigField("EnableAESKey", DefaultValue = false)]

        public bool EnableAESKey;

 

       [IICConfigField("EnableSubscribe", DefaultValue = false)]

        public bool EnableSubscribe;

 

       [IICConfigField("EnableListener", DefaultValue = false)]

        public bool EnableListener;

 

       [IICConfigField("EnableEncrypt", DefaultValue = false)]

        public bool EnableEncrypt;

 

        [IICConfigField("SSODomains",DefaultValue = "")]

        public string SSODomains;

 

        [IICConfigField("IDCAuthKey",DefaultValue = "")]

        public string IDCAuthKey;

 

       [IICConfigField("EnableRmsMsg", DefaultValue = false)]

        public bool EnableRmsMsg;

 

       [IICConfigField("MailClientId", DefaultValue ="01")]

        public string MailClientId;

        

        [IICConfigField("MailAppKey",DefaultValue = "mail_user")]

        public string MailAppKey;

 

       [IICConfigField("MailAppSecret", DefaultValue ="123456")]

        public string MailAppSecret;

 

       [IICConfigItemCollection("Url")]

        publicIICConfigItemCollection<string, IDCUrlConfigItem> Urls;

 

       [IICConfigItemCollection("SqApi")]

        publicIICConfigItemCollection<string, IDCSqApiConfigItem> SqApi;

   }

 

   [IICConfigItem("Url", KeyField = "Key")]

   public class IDCUrlConfigItem : IICConfigItem

   {

        [IICConfigField("Key")]

        public string Key;

        [IICConfigField("Value")]

        public string Value;

   }

   [IICConfigItem("SqApi", KeyField = "Key")]

   public class IDCSqApiConfigItem : IICConfigItem

   {

        [IICConfigField("Key")]

        public string Key;

        [IICConfigField("Value")]

        public string Value;

   }

调试、排查问题

本地调试

打开app.config,把RunMode设成local。尽量本地调试,一步一步跟,毕竟这是最直接最高效的排查问题的方式。

 

但是有些情况下,本地会无法调试,比如你挂了一个EFS的服务,它初始化的时候肯定会去连功能,这时候连不上就会报错。本地没法调试,只能重现问题,看日志了。

UT 单元测试

可以针对各个层级的代码进行测试,功能上也有保障,而且在技术调研、功能测试都可以写单元测试来完成。

构造自己的工具箱

l  例如想测试WEB服务是否工作,可以模拟客户端写一个发请求的页面。

l  再如想重现WEB BAR或者WEB IM的一个问题,就可以用winform写一个模拟客户端(因为我认为这个交互性还是比较强的,单元测试模拟起来和场景有偏差,并且写一个工具今后遇到类似的问题就不用再写单元测试代码了)模拟客户端的操作。

l  构造自己的工具,可以重现出问题,然后可以再查日志或者跟踪代码找原因,再解决问题。

l  可以在开发过程中,积累越来越多的工具(例如http发请求工具,http挡板),简化今后的调试工作,有时也会给测试带来好处。

日志

当问题出现,首先是通过看日志能否找到原因,如果找不到再自己想办法重现问题(通过工具模拟客户端操作),重新找原因。当问题重现的时候,看一下日志为什么没有记,可以把这部分的日志追加上,下次遇到相同的问题就节省的时间。

 

记日志

抛了异常

所有异常的地方一定要记error级别的log

 

调试

尽量多记trace。因为服务无论部署到功能环境还是现网(尤其是现网,日志记录不充分就只能通过抓dump文件来排查问题了),调试是个问题,所以诊断问题通常就靠记的trace。记录日志也不要太过频繁,因为太频繁的I/O操作会影响性能。

 

注意日志级别

不是异常就尽量不要记error级别的log。如果一个业务上的错误是正常允许的并且是频繁发生的,可以记成Warning级别的,如果记成error就会导致巡检里面大片的异常。之前是IDC网盘订购失败了,记成了error级别的错误,结果在巡检上面出现大片异常。

 

彩云服务技术点

XML序列化/反序列化

XML序列化/反序列化。功能上来讲,可以实现XML—实体类之间的映射,并且性能也还可以,10000/3-5秒左右。不过100000次以上序列化的话直接崩溃,如果选择用字符串去拼的话300000次打开10秒左右。所以高并发的情况下,还是要用原始的拼字符串的方法。各有利弊,序列化维护起来容易点,改实体就行了。

 

HTTPListener

HttpListener。可以在服务中起一个监听,监听Http请求,这部分一开始用的MSDN的demo,自己扩展了一下,发现性能太差了,并且很不稳定,目前用的离线文件传输的代码,性能上也不错,大概可以支持1000个请求/每秒。有需要的也可以参考一下。

 

 

EFS事件订阅

检查路由策略

在routePolicy里面检查是否配置了路由策略,确保EFS可以找到自己。

例如IDCS一开始只配置了srv,后来加的id。至于是id还是srv,要看你订阅事件的生产者,比如是从CS那里订阅了一个事件,那么它用的是id就是id。具体要看生产者那个服务的代码。

这个配置完了,要跟运维说刷一下配置。

 

还要注意一点,别忘了更新SiteB上面的路由策略配置,否则在巡检平台上面刘宝那里会报一大堆EFS找不到目标服务的异常。

检查EFS的配置

 

在EFS的配置节中检查是否正确的配置。例如我订阅了RMS事件,就需要去EFS里面配置一下:(在ToServices里面,追加一个IDCS服务即可)

 

EFS配置完了,需要重启EFS服务。

 

调用的代码示例

 

   // "EfsSubscribeArgs"

                string[] efsEventNameList = newstring[] { "RmsMessageEvent" };

               EventConsumer.Initialize(efsEventNameList);

 

 

               EventConsumer.RegisterHandler<RmsMessageArgs>(efsEventNameList[0],Singleton<AddMsgHandler>.Instance.Handle);

                //EventConsumer.RegisterHandler<EfsSubscribeArgs>(efsEventNameList[1],Singleton<AddMsgHandler>.Instance.Handle);

 

                EventConsumer.Start();

 

                Tracing.InfoFmt("eventconsumer started");

 

 

性能

同步和异步编程

l  作为互联网应用的服务端,涉及到调服务,发http请求这些操作,都需要写成异步的。同步和异步的差别是相当明显的,曾经就是因为同步,导致一个任务hang死,其他请求进不来,不得不重构将近60%的代码,把同步改成了异步。

 

l  即使是挡板,记得也要做成异步的。不要觉得挡板无所谓,写一个固定返回值的放在那里就ok了,我第一次做挡板就写成了同步,结果压力一直上不去,只压到了500,当时cpu都已经70%了,后来仔细检查代码发现,挡板是同步的,改成了异步问题解决,1300/每秒,cpu 60%。

 

尽量重用现有的、稳定的技术方案

彩云项目里面有两个技术点:

 

l  发Http请求

已有了EA这个项目,由于EA也是久经考验的项目了,所以第一选择一定是把EA里面的发请求部分搬进来使用,这样节省了时间也降低了风险,还提高了性能。当时还是选择了技术调研,自己绕了大弯子,最后还是选择重用EA的代码。

 

另外海涛提的建议不错,在此分享一下,当给第三方发请求的时候,把请求时间放header里面发过去。

 

l  接收http请求

对于接收http请求这部分,传离线文件的代码也是现成的,并且支持了1000个以上的并发,所以可以直接搬过来用的,当时也走了弯路。

 

记录请求时间和收到应答的时间

和第三方打交道,一个请求慢了,究竟是哪边慢,最好是记录一下请求过去的时间和对方应答的时间。如果没记,就只有抓包了(这次和岳元就上了19层抓包)。同样,如果涉及到客户端访问自己,也记录一下收到请求的时间和应答的时间。这样可以找到处理慢的原因在哪里,再去做相关优化。

 

异常

捕捉异常

提到了异步编程,不得不提异常捕获。异常捕获可以一句话概括,一定要记得捕捉到所有的异常。比如异步监听,新来一个请求就会新起一个线程去处理它,那么在这个新起的线程里面一定要捕获异常,因为外层的try –catch捕捉不到新线程里面的异常,出异常了就会直接crash。

回调

另外,记得所有地方一定要记得回调,因为是异步编程,函数口都会给一个callback,如果不回调,它就会一直hang住。除了异常没捕捉,没有回调也是异步编程很危险的一个地方。

 

上线前注意事项

检查dll列表

检查dll列表和现网是否一致(尤其是引入了新的dll的时候,如ssi,aspnetCommon等),如果不确定,就让现网运维抓一个dll列表下来吧,核对了再上线,上次IDCS因为这个来回折腾了两次。

理清服务调用关系

整理一下代码里都调用了哪些服务(比如调用了IBS),哪些服务调用了自己的服务(比较典型的例子,EFS),这些都需要在现网配置相应的路由策略。比如上次IDCS没有提这些,导致现网运维在这上面浪费了两个小时。

 

配置项

如果新加了配置,先在配置节模板里面添加一下,更新配置节。最好养成一个习惯,每增加一个配置,都记在文档里面,上线的时候把这个文档递给运维就行了。

 

Review代码

Review dll。检查一下代码的版本,以及异常是否全部捕捉。

 

你可能感兴趣的:(编程,String,单元测试,dll,工具,WinForm)