这个问题很紧急,希望知道的朋友能够告诉我,或者来视频或语音交流也在所不惜,因为我这方面的经验实在是太缺乏了...
说下项目的具体环境:
1. Andirons前端点菜,基于IPad的点菜系统
2. WCF后台服务(很简单的WCF服务,就使用了一下ServiceHost开启下服务,发布一下定义的接口)
说下我的问题:
1. 10台IPad同时提交菜单,会出现卡死的现象(这个卡死就是反应时间太长,隔个半天才有反应)
(当时我的猜测是后台窗体是单线程操作,会出现排队等待的现象,是否应该使用异步(多线程)来做)
这种问题是否是并发问题,该怎么解决呢?
2. 如果WCF服务开的时间长了,那么Andirons端在调用的时候会出现延迟的情况
(为什么服务的开的时间越久,Andirons端调用的反应也会越来越慢)
Service重启之后,速度就会非常的快,这到底是什么原因呢?
3. 对于第一个问题,如果每个跟数据库交互的方法都要用异步来做,编程困难会不会很大
下面是我的AppConfig文件
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="LoggingManager" type="Transight.Common.Logging.LoggingManagerSection,Transight.Common.Logging"/> <section name="DBAccessBroker" type="Transight.Common.DBAccessBroker.DBAccessBrokerSection,Transight.Common.DBAccessBroker"/> <section name="MessageBusService" type="Transight.Common.MessageBus.Transports.TransportSettings,Transight.Common.MessageBus.Transports"/> </configSections> <system.serviceModel> <services> <service name="Transight.POS.FrontendService.JsonService.Operations" behaviorConfiguration="FrontendService"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpBinding_Config" contract="Transight.POS.FrontendService.Contracts.IJsonOperation" > </endpoint> <host> <baseAddresses> <add baseAddress="http://localhost/FrontendWebService/"/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="FrontendService"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="httpBinding_Config" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" messageEncoding="Text" sendTimeout="00:10:00"> <security mode="None"></security> <readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647"/> </binding> </basicHttpBinding> </bindings> <serviceHostingEnvironment multipleSiteBindingsEnabled="false" /> </system.serviceModel> <LoggingManager> <Loggings> <add LoggingName="Default" LoggingEntity="FileLogging" LogFilePath="c:\Log" LogSeparateByLevel="true" LogFileName="Transight.POSV4.Logs"/> <!--<add LoggingName="PluginRuntime" LoggingEntity="FileLogging" LogFilePath="c:\Log" LogSeparateByLevel="true" LogFileNamee="Transight.POSV4.Logs" LogSource="PluginRuntime"/> <add LogSource="PrintingMgr" LoggingName="PrintingMgrLog" LoggingEntity="FileLogging" LogFilePath="c:\Log" LogSeparateByLevel="true" LogFileName="Transight.POSV4.Logs"/> <add LogSource="PrintingJob" LoggingName="PrintingJobLog" LoggingEntity="FileLogging" LogFilePath="c:\Log" LogSeparateByLevel="true" LogFileName="Transight.POSV4.Logs"/>--> </Loggings> </LoggingManager> <DBAccessBroker> <DBAccess FullName="Transight.Common.DBAccess.SQLDBAccess" Namespace ="Transight.Common.DBAccess"/> <DBConnections> <add ConfigFile="C:\CommonConfig.xml" ConnStringNode="ConnStr" IsConnStrEncrypted="false" SystemName="Tablet" IsServer="false"/> </DBConnections> </DBAccessBroker> <MessageBusService ServiceAddress="net.tcp://192.168.23.50:8787/MessageBusService" /> </configuration>
1,windows 2003/2008之类的server会默认开启优化后台服务,所以你的Winform应用程序优先级不高;
2,自宿主的 wcf 应用需要开启gc server模式;
3,启动 ServiceHost 的操作放置在非UI线程中;
第二个和第三个回答费解,能否有文章参考...
@程序猿就是我: 在你的配置文件中:<runtime><gcServer enable="true"></gcServer> </runtime>
@程序猿.码农: 说实话,我实在不明白你所谓的卡死是啥意思,所以我猜测是你的宿主程序UI失去了响应,因此我建议你new Thread ,然后在线程回掉中启动服务,这只能加速UI响应。数据库异步也简单,自己封装个AsyncResult就行,SqlCommand也提供了BeginExecute方法。
@程序猿.码农: 是的,我发现寄宿WCF服务的窗体都发白了,如,出现了无反应的情况!
@程序猿就是我: 你可以先通过在新线程中宿主服务来改善你的UI响应。
@程序猿.码农: 那么代码是不是这样写:
ThreadPool.QueueUserWorkItem(StartService);
下面是启动WCF服务的方法
private void StartService(object obj)
{
ServiceHost host = new ServiceHost(typeof(JsonService.Operations));
host.Open();
}
这样的写法可以吗?还是我要使用Thread才行!
@程序猿就是我: 这样不行,host出了作用域就释放了。你的 host.Open()后,将当前线程阻塞住。
private void StartService(object obj)
{
ServiceHost host = ....
host.Open();
WaitForSingleObject(event); // 这是伪码,阻塞当前线程,等待信号。
host.Close();
}
当然,这样并不能完全的解决问题,你还需要检查你的 service 里哪些方法占用了UI线程。
@程序猿.码农:
WaitForSingleObject(event);
这句代码的意思是我另写一个方法,阻止当前线程!
可是我的当前线程还需要做其它一些工作!这样两者会冲突吗?
@程序猿就是我: 在C#,可以使用 ManualResetEvent,ManualResetEvent.WaitOne 来等待有其它线程触发 Event。你当前线程除了host.Open(),还要做啥?是必须紧跟host.Open()后串行执行的吗?
@程序猿.码农:
Open()方法后面不执行东西,就是我的WCF服务开起来之后,会有很多客户端连过来,后台有个时钟会一直跑,刷新有哪些客户端连过来了,然后把客户端的基本数据显示在窗体上...
就这么多了!
@程序猿就是我: 你如何实现你的显示客户端连接信息的?Timer的频率是多少?
@程序猿.码农: 客户端会传一个ClientName参数过来,先验证是否有效,如果正确就去数据库获取相应的客户端信息,然后放到一个字典类中,最后显示在窗体上面!
时钟频率是5秒!
不知道我的描述是否可以啊!
@程序猿就是我: 说实话,不太明白,你把重要的信息都遗漏了。客户端每次调用都传递一个ClientName参数吗?客户端每次调用都会触发服务器同步查询数据库吗?Timer每5秒直接从字典读数据显示到UI吗?
@程序猿.码农:
不好意思我说的不够清楚,服务端有很多方法,每个方法都有一个clientname参数,只有第一次连接时候才去数据库获取这个clientname相关的信息,显示到窗体上,当连接成功后客户端点菜的信息也会显示到窗体上面!时钟就是每五秒从字典读数据!
顺便问一下<runtime><gcServer enable="true"></gcServer> </runtime>
这个节点我找不到,是随便写的吗?
@程序猿就是我: http://msdn.microsoft.com/zh-cn/library/0kk0kk35
你是通过字典里是否有clientName来判断是否为第一连接的吗?
另外,你new的Thread,设置为Background,线程单元模型设置为 MTA。
对。可以 放在console中。
这个好像不现实了,项目已经做的快好了,现在该宿主会很麻烦的!
@程序猿就是我: 是不是 宿主 有何 ui 进行交互的地方呢?按理说 开几个 是没有问题的哦。本来 就是多线程哦。除非 有共享资源哦。
@無限遐想: 宿主 改 一下。怎么会麻烦呢?你直接在program理,直接启动就好了。不要调用窗体。加一个console.readline()
@無限遐想: 哪有这么容易就能改变啦,因为这个窗体里面还有一些操作!
1,把配置拿出来看看,并发10个不算什么。
2,说明程序写的有问题,有资源没释放或者内存泄露的情况,很有可能数据库连接用完没释放。
3,推测点菜什么的数据量不大,逻辑也不耗时,没必要异步。
4,如上,另外用个非UI线程用来跑host
你想看AppConfig文件吗?
配置文件已经贴上去了,求指教!
@程序猿就是我:
没看到特殊的关于并发的配置,默认并发最大为10,看看artch的神作http://www.cnblogs.com/artech/archive/2010/03/19/1690118.html,关于并发的,应该可以找到解决方法。我觉得程序可能写的有问题,我写的专门计算数据的,上来内存就几百兆,没日没夜的跑都没事。
把servicehost放非UI线程很重要
@arg: 谢谢你啊,我会认真看文章的,有问题我再联系你啊!
寄宿在IIS吧,自寄宿的EXE,默认都是单线程处理的。。。。。。。。。。。。
所以越来越慢。。。。。。。。。
那现在只能寄宿在窗体中了,有什么好的优化方案吗?对于越来越慢的问题,有没有好的方案...
大哥,有没有什么好点子告诉我,一直没有接触过服务编程啊,求解!
应用[ServiceBehavior(UseSynchronizationContext = false)]
这个特性是放在我发布的接口上面还是实现接口的类上面?老A
@程序猿就是我: Service类型
@Artech: 老A,除了上面的建议之外,对于我写出来的问题,你还有什么建议指点给我呀?谢谢了!
你选择winform,肯定是有业务需要的。业务流程是不是这样的,前端选择菜,通过wcf传输给后端,后端通过winform显示客户点的菜,然后后厨看看有没有原材料了,也就是能否接受这个点菜,接受与否都会操作一下,然后你的前端会看到效果,决定继续点菜还是告诉客人换菜,后厨做好菜了,就通过winform操作一下,前端收到菜已做好的信号,就去拿菜给客人,业务流程是不是这样的呢?整个过程需要后端winform和前端的交互。所以你选择winform作为host。
对的,其实你说的业务流比较复杂了,我们后台紧紧查看那每个ipad的状态,以及点了多少菜!
我想请教下,怎么通过代码解决一下并发的问题呀!
@程序猿就是我: 其实你说的那些操作我们还有一个POS机系统来进行控制!
@程序猿就是我: 现在我的做法是:
1.把服务单独放到一个线程里面
Thread thread = new Thread(StartService) { IsBackground = true };
thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
2.然后设置并发模式以及实例模式
[ServiceBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
3.在配置文件里面增加并发量连接数
<serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
不知道这些配置能不能够满足这些配置。。。