Search in sources :

Example 1 with DeferredResultWrapper

use of com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper in project apollo by ctripcorp.

the class NotificationControllerV2Test method setUp.

@Before
public void setUp() throws Exception {
    controller = new NotificationControllerV2();
    gson = new Gson();
    when(bizConfig.releaseMessageNotificationBatch()).thenReturn(100);
    when(bizConfig.releaseMessageNotificationBatchIntervalInMilli()).thenReturn(5);
    ReflectionTestUtils.setField(controller, "releaseMessageService", releaseMessageService);
    ReflectionTestUtils.setField(controller, "entityManagerUtil", entityManagerUtil);
    ReflectionTestUtils.setField(controller, "namespaceUtil", namespaceUtil);
    ReflectionTestUtils.setField(controller, "watchKeysUtil", watchKeysUtil);
    ReflectionTestUtils.setField(controller, "gson", gson);
    ReflectionTestUtils.setField(controller, "bizConfig", bizConfig);
    someAppId = "someAppId";
    someCluster = "someCluster";
    defaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
    defaultNamespace = ConfigConsts.NAMESPACE_APPLICATION;
    somePublicNamespace = "somePublicNamespace";
    someDataCenter = "someDC";
    someNotificationId = 1;
    someClientIp = "someClientIp";
    when(namespaceUtil.filterNamespaceName(defaultNamespace)).thenReturn(defaultNamespace);
    when(namespaceUtil.filterNamespaceName(somePublicNamespace)).thenReturn(somePublicNamespace);
    when(namespaceUtil.normalizeNamespace(someAppId, defaultNamespace)).thenReturn(defaultNamespace);
    when(namespaceUtil.normalizeNamespace(someAppId, somePublicNamespace)).thenReturn(somePublicNamespace);
    deferredResults = (Multimap<String, DeferredResultWrapper>) ReflectionTestUtils.getField(controller, "deferredResults");
}
Also used : Gson(com.google.gson.Gson) DeferredResultWrapper(com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper) Before(org.junit.Before)

Example 2 with DeferredResultWrapper

use of com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper in project apollo by ctripcorp.

the class NotificationControllerV2 method handleMessage.

@Override
public void handleMessage(ReleaseMessage message, String channel) {
    logger.info("message received - channel: {}, message: {}", channel, message);
    String content = message.getMessage();
    Tracer.logEvent("Apollo.LongPoll.Messages", content);
    if (!Topics.APOLLO_RELEASE_TOPIC.equals(channel) || Strings.isNullOrEmpty(content)) {
        return;
    }
    String changedNamespace = retrieveNamespaceFromReleaseMessage.apply(content);
    if (Strings.isNullOrEmpty(changedNamespace)) {
        logger.error("message format invalid - {}", content);
        return;
    }
    if (!deferredResults.containsKey(content)) {
        return;
    }
    // create a new list to avoid ConcurrentModificationException
    List<DeferredResultWrapper> results = Lists.newArrayList(deferredResults.get(content));
    ApolloConfigNotification configNotification = new ApolloConfigNotification(changedNamespace, message.getId());
    configNotification.addMessage(content, message.getId());
    // do async notification if too many clients
    if (results.size() > bizConfig.releaseMessageNotificationBatch()) {
        largeNotificationBatchExecutorService.submit(() -> {
            logger.debug("Async notify {} clients for key {} with batch {}", results.size(), content, bizConfig.releaseMessageNotificationBatch());
            for (int i = 0; i < results.size(); i++) {
                if (i > 0 && i % bizConfig.releaseMessageNotificationBatch() == 0) {
                    try {
                        TimeUnit.MILLISECONDS.sleep(bizConfig.releaseMessageNotificationBatchIntervalInMilli());
                    } catch (InterruptedException e) {
                    // ignore
                    }
                }
                logger.debug("Async notify {}", results.get(i));
                results.get(i).setResult(configNotification);
            }
        });
        return;
    }
    logger.debug("Notify {} clients for key {}", results.size(), content);
    for (DeferredResultWrapper result : results) {
        result.setResult(configNotification);
    }
    logger.debug("Notification completed");
}
Also used : DeferredResultWrapper(com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper) ApolloConfigNotification(com.ctrip.framework.apollo.core.dto.ApolloConfigNotification)

Example 3 with DeferredResultWrapper

use of com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper in project apollo by ctripcorp.

the class NotificationControllerV2 method pollNotification.

@RequestMapping(method = RequestMethod.GET)
public DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> pollNotification(@RequestParam(value = "appId") String appId, @RequestParam(value = "cluster") String cluster, @RequestParam(value = "notifications") String notificationsAsString, @RequestParam(value = "dataCenter", required = false) String dataCenter, @RequestParam(value = "ip", required = false) String clientIp) {
    List<ApolloConfigNotification> notifications = null;
    try {
        notifications = gson.fromJson(notificationsAsString, notificationsTypeReference);
    } catch (Throwable ex) {
        Tracer.logError(ex);
    }
    if (CollectionUtils.isEmpty(notifications)) {
        throw new BadRequestException("Invalid format of notifications: " + notificationsAsString);
    }
    DeferredResultWrapper deferredResultWrapper = new DeferredResultWrapper();
    Set<String> namespaces = Sets.newHashSet();
    Map<String, Long> clientSideNotifications = Maps.newHashMap();
    Map<String, ApolloConfigNotification> filteredNotifications = filterNotifications(appId, notifications);
    for (Map.Entry<String, ApolloConfigNotification> notificationEntry : filteredNotifications.entrySet()) {
        String normalizedNamespace = notificationEntry.getKey();
        ApolloConfigNotification notification = notificationEntry.getValue();
        namespaces.add(normalizedNamespace);
        clientSideNotifications.put(normalizedNamespace, notification.getNotificationId());
        if (!Objects.equals(notification.getNamespaceName(), normalizedNamespace)) {
            deferredResultWrapper.recordNamespaceNameNormalizedResult(notification.getNamespaceName(), normalizedNamespace);
        }
    }
    if (CollectionUtils.isEmpty(namespaces)) {
        throw new BadRequestException("Invalid format of notifications: " + notificationsAsString);
    }
    Multimap<String, String> watchedKeysMap = watchKeysUtil.assembleAllWatchKeys(appId, cluster, namespaces, dataCenter);
    Set<String> watchedKeys = Sets.newHashSet(watchedKeysMap.values());
    List<ReleaseMessage> latestReleaseMessages = releaseMessageService.findLatestReleaseMessagesGroupByMessages(watchedKeys);
    /**
     * Manually close the entity manager.
     * Since for async request, Spring won't do so until the request is finished,
     * which is unacceptable since we are doing long polling - means the db connection would be hold
     * for a very long time
     */
    entityManagerUtil.closeEntityManager();
    List<ApolloConfigNotification> newNotifications = getApolloConfigNotifications(namespaces, clientSideNotifications, watchedKeysMap, latestReleaseMessages);
    if (!CollectionUtils.isEmpty(newNotifications)) {
        deferredResultWrapper.setResult(newNotifications);
    } else {
        deferredResultWrapper.onTimeout(() -> logWatchedKeys(watchedKeys, "Apollo.LongPoll.TimeOutKeys"));
        deferredResultWrapper.onCompletion(() -> {
            // unregister all keys
            for (String key : watchedKeys) {
                deferredResults.remove(key, deferredResultWrapper);
            }
            logWatchedKeys(watchedKeys, "Apollo.LongPoll.CompletedKeys");
        });
        // register all keys
        for (String key : watchedKeys) {
            this.deferredResults.put(key, deferredResultWrapper);
        }
        logWatchedKeys(watchedKeys, "Apollo.LongPoll.RegisteredKeys");
        logger.debug("Listening {} from appId: {}, cluster: {}, namespace: {}, datacenter: {}", watchedKeys, appId, cluster, namespaces, dataCenter);
    }
    return deferredResultWrapper.getResult();
}
Also used : ReleaseMessage(com.ctrip.framework.apollo.biz.entity.ReleaseMessage) BadRequestException(com.ctrip.framework.apollo.common.exception.BadRequestException) ApolloConfigNotification(com.ctrip.framework.apollo.core.dto.ApolloConfigNotification) DeferredResultWrapper(com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper) Map(java.util.Map) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 4 with DeferredResultWrapper

use of com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper in project apollo by ctripcorp.

the class NotificationControllerV2Test method assertWatchKeys.

private void assertWatchKeys(Multimap<String, String> watchKeysMap, DeferredResult deferredResult) {
    for (String watchKey : watchKeysMap.values()) {
        Collection<DeferredResultWrapper> deferredResultWrappers = deferredResults.get(watchKey);
        boolean found = false;
        for (DeferredResultWrapper wrapper : deferredResultWrappers) {
            if (Objects.equals(wrapper.getResult(), deferredResult)) {
                found = true;
            }
        }
        assertTrue(found);
    }
}
Also used : DeferredResultWrapper(com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper)

Aggregations

DeferredResultWrapper (com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper)4 ApolloConfigNotification (com.ctrip.framework.apollo.core.dto.ApolloConfigNotification)2 ReleaseMessage (com.ctrip.framework.apollo.biz.entity.ReleaseMessage)1 BadRequestException (com.ctrip.framework.apollo.common.exception.BadRequestException)1 Gson (com.google.gson.Gson)1 Map (java.util.Map)1 Before (org.junit.Before)1 RequestMapping (org.springframework.web.bind.annotation.RequestMapping)1