【问题标题】:PushSharp's StopAllServices() hangs as Windows ServicePushSharp 的 StopAllServices() 作为 Windows 服务挂起
【发布时间】:2013-05-18 18:58:12
【问题描述】:

我正在尝试让 PushSharp (2.0.4.0?) 作为 Windows 服务运行。每次服务运行并执行 push.QueueNotification() 时,applet 都会挂起对 stopAPNS(push) 的调用。当我运行与控制台应用程序类似的代码时,它每次都运行良好(即,我正在接收我的推送通知)。控制台应用基于 PushSharp.Sample 项目。

有趣的是,如果我在没有执行任何 push.QueueNotification() 调用的情况下启动/停止我的小程序,我的服务将正确退出。在其他情况下,对 OnStop() 中的 stopAPNS(push) 的调用不会挂起。我想这是有道理的……队列是空的

我在 .NET 4.0 和 4.5 下编译了 PushSharp 项目。在每种情况下,我都会得到相同的行为。

我在下面提供了我的代码的净化版本。

关于为什么 push.StopAllServices(true) 调用在作为 Windows 服务运行时挂起的任何想法?

谢谢。

public class PushNoteService : System.ServiceProcess.ServiceBase
{
// Initialize global instance of PushBroker service
    private PushBroker push = new PushBroker();

    #region Constructor
    public PushNoteService()
    {
        // This call is required by the Windows.Forms Component Designer.
        InitializeComponent();

        // TODO: Add any initialization after the InitComponent call
    }
    #endregion

    #region Component Designer generated code

    // The main entry point for the process
    static void Main()
    {
        System.ServiceProcess.ServiceBase[] ServicesToRun;

        // More than one user Service may run within the same process. To add
        // another service to this process, change the following line to
        // create a second service object. For example,
        //
        //   ServicesToRun = new System.ServiceProcess.ServiceBase[] {new PushNoteService(), new MySecondUserService()};
        //
        ServicesToRun = new System.ServiceProcess.ServiceBase[] { new PushNoteService() };

        System.ServiceProcess.ServiceBase.Run(ServicesToRun);
    }

    #endregion

    #region OnStart
    /// <summary>
    /// Set things in motion so your service can do its work.
    /// </summary>
    protected override void OnStart(string[] args)
    {
        YYLog.Log.Instance.Info("Starting service.");

        timer2.Enabled = true;

        YYLog.Log.Instance.Info("Starting APNS.");
        startAPNS(push);
    }
    #endregion

    #region OnStop
    /// <summary>
    /// Stop this service.
    /// </summary>
    protected override void OnStop()
    {
        YYLog.Log.Instance.Info("Stopping service.");

        // Add code here to perform any tear-down necessary to stop your service.
        timer2.Enabled = false;

        YYLog.Log.Instance.Info("Stopping APNS.");
        stopAPNS(push);

        // some clean up.
        push = null;

        YYLog.Log.Instance.Info("Service stopped.");
    }
    #endregion


    #region Acess Methods


    /// <summary>
    /// On Timer_Elasped events, websites are accessed to check status.
    /// </summary>
    /// <param name="sender">Sender.</param>
    /// <param name="e">E.</param>
    private void timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
            int count = checkForPushRequest();
            YYLog.Log.Instance.Info("   Count: " + count.ToString());
            if (count > 0)
            {
                stopAPNS(push);
            }

    }

    private int checkForPushRequest()
    {
        YYLog.Log.Instance.Info("Processing push notifications...");

        int count = 0;

        // Get the ConnectionStrings collection.
        ConnectionStringSettings connections = ConfigurationManager.ConnectionStrings["MyDB"];

        using (SqlConnection conn = new SqlConnection(connections.ConnectionString))
        {
            conn.Open();

            SqlCommand cmd = new SqlCommand("MySP", conn);
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            SqlDataReader dr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);

            while (dr.Read())
            {
                // increment counter
                count++;

                int badgeNumber = 0;

                string deviceToken = Convert.ToString(dr[dr.GetOrdinal("DeviceToken")]);
                string alertMessage = Convert.ToString(dr[dr.GetOrdinal("AlertMessage")]);
                if (!dr.IsDBNull(dr.GetOrdinal("BadgeNumber")))
                {
                    badgeNumber = Convert.ToInt16(dr[dr.GetOrdinal("BadgeNumber")]);
                }
                string soundFile = Convert.ToString(dr[dr.GetOrdinal("SoundFile")]);

                // Send out the notification to APNS
                YYLog.Log.Instance.Trace("  Sending notification to " + deviceToken);
                sendPush(deviceToken, alertMessage, badgeNumber, soundFile);
            }

            dr.Close();
        }

        return count;
    }

    private void sendPush(string DeviceToken, string AlertMessage, int BadgeNumber, string SoundFile)
    {
        push.QueueNotification(new AppleNotification()
                                   .ForDeviceToken(DeviceToken)
                                   .WithAlert(AlertMessage)
                                   .WithBadge(BadgeNumber)
                                   .WithSound(SoundFile));

    }

    private void startAPNS(PushBroker push)
    {
        //Wire up the events for all the services that the broker registers
        push.OnNotificationSent += NotificationSent;
        push.OnChannelException += ChannelException;
        push.OnServiceException += ServiceException;
        push.OnNotificationFailed += NotificationFailed;
        push.OnDeviceSubscriptionExpired += DeviceSubscriptionExpired;
        push.OnDeviceSubscriptionChanged += DeviceSubscriptionChanged;
        push.OnChannelCreated += ChannelCreated;
        push.OnChannelDestroyed += ChannelDestroyed;

        string appleCertFileName = System.Configuration.ConfigurationManager.AppSettings["APNS_Certificate"];
        var appleCert = File.ReadAllBytes(appleCertFileName);

        string appleCertPassword = System.Configuration.ConfigurationManager.AppSettings["APNS_Certificate_Password"];
        bool productionMode = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["APNS_Production_Mode"]);
        push.RegisterAppleService(new ApplePushChannelSettings(productionMode, appleCert, appleCertPassword)); //Extension method
    }

    private void stopAPNS(PushBroker push)
    {
        YYLog.Log.Instance.Info("Waiting for Queue to Finish...");

        //Stop and wait for the queues to drains
        push.StopAllServices(true);

        YYLog.Log.Instance.Info("Queue Finished");
    }

    #region Events
    private void DeviceSubscriptionChanged(object sender, string oldSubscriptionId, string newSubscriptionId, INotification notification)
    {
        //Currently this event will only ever happen for Android GCM
        YYLog.Log.Instance.Info("Device Registration Changed:  Old-> " + oldSubscriptionId + "  New-> " + newSubscriptionId + " -> " + notification);
    }

    private void NotificationSent(object sender, INotification notification)
    {
        YYLog.Log.Instance.Info("Sent: " + sender + " -> " + notification);
    }

    private void NotificationFailed(object sender, INotification notification, Exception notificationFailureException)
    {
        YYLog.Log.Instance.Error("Failure: " + sender + " -> " + notificationFailureException.Message + " -> " + notification);
    }

    private void ChannelException(object sender, IPushChannel channel, Exception exception)
    {
        YYLog.Log.Instance.Error("Channel Exception: " + sender + " -> " + exception);
    }

    private void ServiceException(object sender, Exception exception)
    {
        YYLog.Log.Instance.Error("Channel Exception: " + sender + " -> " + exception);
    }

    private void DeviceSubscriptionExpired(object sender, string expiredDeviceSubscriptionId, DateTime timestamp, INotification notification)
    {
        YYLog.Log.Instance.Info("Device Subscription Expired: " + sender + " -> " + expiredDeviceSubscriptionId);
    }

    private void ChannelDestroyed(object sender)
    {
        YYLog.Log.Instance.Info("Channel Destroyed for: " + sender);
    }

    private void ChannelCreated(object sender, IPushChannel pushChannel)
    {
        YYLog.Log.Instance.Info("Channel Created for: " + sender);
    }
    #endregion

    #endregion

}

【问题讨论】:

  • 尝试为 PushBroker 实例连接一些事件,看看您是否真的收到了消息发送或错误等。
  • @Redth,在 startAPNS() 中,事件被连接在那里。您可以在代码 sn-p 的底部看到事件。小程序和控制台应用程序中的代码用于连接事件是相同的。控制台应用程序返回结果,即调用 NotificationSent()。在小程序中,它不被调用。我是否正确理解了您的评论?谢谢。
  • 所以没有发生错误?我真的需要更多信息来帮助诊断。难道它“挂起”真的是因为它正在等待发送消息时队列被耗尽?
  • @Redth,没有错误。这就是使它成为一个谜的原因。该队列的消息不超过 5 条(我正在使用一个小子集进行测试)。因此,它不应少于一分钟。当我通过控制台应用程序运行相同的测试时,它会在 10 秒内刷新。只是想弄清楚其中的区别。您有作为 Windows 服务运行的 PushSharp 的示例吗?谢谢。
  • 我没有例子,因为它应该没有什么不同。我将建议您从源代码编译并设置一些断点并进行一些调试以尝试看看发生了什么:)

标签: push-notification pushsharp


【解决方案1】:

我们有一个 Web 应用程序,但我认为解决方案可以是相同的。 我们花了一整天的时间来猜测问题所在! 最终是在错误的 Newtonsoft.Json 版本中 我们解决方案中的一些项目依赖于这个库的旧版本,因此我们很不幸在 Web 项目的 /bin 文件夹中得到了错误的版本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多