Notifications - Push, Pull, Stream Notification #3
Exchange 2013을 기반으로 개발을 진행하면서 맞닥뜨리게 된 케이스에 대해서 공유 하고자 한다. 이전에 잠깐 EWS(Exchange Web Service)를 통해 간단한 기능을 사용해본 경험이 전부라서 하나의 기능을 구현하기 위해 여러가지 방안에 대해서 바위에 계란치기로 직접 부딪혀 볼 수 밖에 없었다. 이런 힘들고 시간 싸움을 줄이는데 도움을 드리는데 일조 하고자 이곳에 공유 하고자 합니다. 비록 덜 정제 되고 문서 미비할 수 있지만 참고 사항으로 알아 두셨으면 합니다. |
이번 포스트에서는 지난 시간에 다루지 못했던 프로그래머 관점에서 살펴 보고자 한다. Pull, Push, Stream 방식은 가각의 장단점이 있으니 적절히 판단하여 실 환경에 적용해야 하겠습니다. 각 장단점은 이전 포스트의 첫번째에서 비교를 해 놓았습니다.
1. Stream notifications
#region Streaming notifications test ExchangeService serviceConnection = EWSHelper.GetService(); //EWS를 반환 한다. StreamingSubscriptionConnection streamConnection = new StreamingSubscriptionConnection(serviceConnection, 30); //Timeout은 1 ~ 30분, 하나의 커넥션을 만든다. FolderId[] foldersToWatch = null; StreamingSubscription streamingsubscription = null; foreach (var member in Members.GetUsers()) { //비동기 상에서 overwrap이 되지 않도록 처리 var m = member; var task = System.Threading.Tasks.Task.Factory.StartNew(() => { Log.WriteLine(m + " : Subscription 등록중"); #region Server와 Impersonate를 진행 ExchangeService service = EWSHelper.GetService(); //GetService(member); //가장할 사용자 정보 service.ImpersonatedUserId = new ImpersonatedUserId() { Id = m, IdType = ConnectingIdType.SmtpAddress }; //구독에 필요한 정보를 세팅 StreamingSubscription streamSubscription = service.SubscribeToStreamingNotifications( new FolderId[] { WellKnownFolderName.Inbox }, EventType.NewMail, EventType.Modified ); //connection에 subscription를 추가하여 이벤트를 받을 수 있도록 함. //하나의 커넥션의 여러개의 Subscription을 추가한다. 기본적으로 StreamConnection은 2000를 넘으면 서버에서 Busy Exception을 발생시킨다. streamConnection.AddSubscription(streamSubscription); Log.WriteLine(m + " : Subscription 등록 완료"); }); tasks.Add(task); } #endregion //모든 비동기 겍체가 완료가 될때까지 대기 함. System.Threading.Tasks.Task.WaitAll(tasks.ToArray()); //Connections.TryAdd("Connection1", streamConnection); // 이벤트 등록 streamConnection.OnNotificationEvent += streamConnection_OnNotificationEvent; streamConnection.OnSubscriptionError += streamConnection_OnSubscriptionError; streamConnection.OnDisconnect += streamConnection_OnDisconnect; streamConnection.Open();
[코드1] Stream notification을 등록 요청 및 이벤트 핸들러 등록
아래 "코드2"에서는 위에서 등록한 이벤트 핸들러에 대한 코드다.
/// <summary> /// Subscription 설정에 따른 Notification 이벤트 발생 /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void streamConnection_OnNotificationEvent(object sender, NotificationEventArgs args) { var task = System.Threading.Tasks.Task.Factory.StartNew(() => { var message = string.Empty; var id = args.Subscription.Service.ImpersonatedUserId.Id; message = id + ";"; #region Binding된 데이터를 가지고 테스트 IEnumerable<ItemId> itemEvents = from e in args.Events.OfType<ItemEvent>() select e.ItemId; //Item.Bind(args.Subscription.Service, itemId) //하나의 객체에 대해서만 바인딩 var response = args.Subscription.Service.BindToItems(itemEvents, new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.From)); var items = response.Select(itemResponse => itemResponse.Item); foreach (var item in items) { EmailMessage msg = item as EmailMessage; if (msg != null) { message += msg.From.Address + "," + item.Id + "," + DateTime.Now.ToString() + ",watermark=" + args.Subscription.Watermark; } } #endregion
Log.WriteLine(message); }); } /// <summary> /// Subscription 관련 에러 이벤트 발생 /// </summary> /// <param name="sender"></param> /// <param name="args"></param> void streamConnection_OnSubscriptionError(object sender, SubscriptionErrorEventArgs args) { } /// <summary> /// Subscription Connection 설정 시간(최대 30분)이 지나서 Disconnect 이벤트 발생하면, Connection 재 연결 처리 /// </summary> /// <param name="sender"></param> /// <param name="args"></param> void streamConnection_OnDisconnect(object sender, SubscriptionErrorEventArgs args) { StreamingSubscriptionConnection connection = (StreamingSubscriptionConnection)sender; if (connection != null) { if (!connection.IsOpen) { //Connection이 끊어지면 다시 오픈하여 이벤트를 받을 수 있도록 함. connection.Open(); } } }
[코드2] 이벤트 핸들러
위 코드에서 streamConnection_OnDisconnection 이벤트는 StreamConnection이 Timeout인해(등록시 30분으로 세팅하여 연결하였고 30분 지나면 OnDisconnection 발생 후 종료 한다) 연결이 끊어진다. 그래서 이 이벤트가 발생하면 계속 유지가 되도록 코딩이 되어 있다. OnNotificationEvent는 일반적인 시나리오에서 발생하는 이벤트이며 이곳에서 아이템에 대한 상태를 판단할 수 있다. 그리고 더 많은 정보를 가져오기 위해서는 Bind를 통해서 한번 더 서버에 접근해야 추가 정보를 가져올 수 있다.