Search in sources :

Example 31 with ApolloConfigNotification

use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.

the class NotificationControllerV2 method pollNotification.

@GetMapping
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);
    }
    Map<String, ApolloConfigNotification> filteredNotifications = filterNotifications(appId, notifications);
    if (CollectionUtils.isEmpty(filteredNotifications)) {
        throw new BadRequestException("Invalid format of notifications: " + notificationsAsString);
    }
    DeferredResultWrapper deferredResultWrapper = new DeferredResultWrapper(bizConfig.longPollingTimeoutInMilli());
    Set<String> namespaces = Sets.newHashSetWithExpectedSize(filteredNotifications.size());
    Map<String, Long> clientSideNotifications = Maps.newHashMapWithExpectedSize(filteredNotifications.size());
    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);
        }
    }
    Multimap<String, String> watchedKeysMap = watchKeysUtil.assembleAllWatchKeys(appId, cluster, namespaces, dataCenter);
    Set<String> watchedKeys = Sets.newHashSet(watchedKeysMap.values());
    /**
     * 1、set deferredResult before the check, for avoid more waiting
     * If the check before setting deferredResult,it may receive a notification the next time
     * when method handleMessage is executed between check and set deferredResult.
     */
    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);
    /**
     * 2、check new release
     */
    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);
    }
    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) GetMapping(org.springframework.web.bind.annotation.GetMapping)

Example 32 with ApolloConfigNotification

use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.

the class NotificationControllerV2 method filterNotifications.

private Map<String, ApolloConfigNotification> filterNotifications(String appId, List<ApolloConfigNotification> notifications) {
    Map<String, ApolloConfigNotification> filteredNotifications = Maps.newHashMap();
    for (ApolloConfigNotification notification : notifications) {
        if (Strings.isNullOrEmpty(notification.getNamespaceName())) {
            continue;
        }
        // strip out .properties suffix
        String originalNamespace = namespaceUtil.filterNamespaceName(notification.getNamespaceName());
        notification.setNamespaceName(originalNamespace);
        // fix the character case issue, such as FX.apollo <-> fx.apollo
        String normalizedNamespace = namespaceUtil.normalizeNamespace(appId, originalNamespace);
        // which means we should record FX.apollo = 1 here and ignore fx.apollo = 2
        if (filteredNotifications.containsKey(normalizedNamespace) && filteredNotifications.get(normalizedNamespace).getNotificationId() < notification.getNotificationId()) {
            continue;
        }
        filteredNotifications.put(normalizedNamespace, notification);
    }
    return filteredNotifications;
}
Also used : ApolloConfigNotification(com.ctrip.framework.apollo.core.dto.ApolloConfigNotification)

Example 33 with ApolloConfigNotification

use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.

the class NotificationController 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;
    }
    List<String> keys = STRING_SPLITTER.splitToList(content);
    // message should be appId+cluster+namespace
    if (keys.size() != 3) {
        logger.error("message format invalid - {}", content);
        return;
    }
    ResponseEntity<ApolloConfigNotification> notification = new ResponseEntity<>(new ApolloConfigNotification(keys.get(2), message.getId()), HttpStatus.OK);
    if (!deferredResults.containsKey(content)) {
        return;
    }
    // create a new list to avoid ConcurrentModificationException
    List<DeferredResult<ResponseEntity<ApolloConfigNotification>>> results = Lists.newArrayList(deferredResults.get(content));
    logger.debug("Notify {} clients for key {}", results.size(), content);
    for (DeferredResult<ResponseEntity<ApolloConfigNotification>> result : results) {
        result.setResult(notification);
    }
    logger.debug("Notification completed");
}
Also used : ResponseEntity(org.springframework.http.ResponseEntity) ApolloConfigNotification(com.ctrip.framework.apollo.core.dto.ApolloConfigNotification) DeferredResult(org.springframework.web.context.request.async.DeferredResult)

Example 34 with ApolloConfigNotification

use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.

the class NotificationController method pollNotification.

/**
 * For single namespace notification, reserved for older version of apollo clients
 *
 * @param appId          the appId
 * @param cluster        the cluster
 * @param namespace      the namespace name
 * @param dataCenter     the datacenter
 * @param notificationId the notification id for the namespace
 * @param clientIp       the client side ip
 * @return a deferred result
 */
@GetMapping
public DeferredResult<ResponseEntity<ApolloConfigNotification>> pollNotification(@RequestParam(value = "appId") String appId, @RequestParam(value = "cluster") String cluster, @RequestParam(value = "namespace", defaultValue = ConfigConsts.NAMESPACE_APPLICATION) String namespace, @RequestParam(value = "dataCenter", required = false) String dataCenter, @RequestParam(value = "notificationId", defaultValue = "-1") long notificationId, @RequestParam(value = "ip", required = false) String clientIp) {
    // strip out .properties suffix
    namespace = namespaceUtil.filterNamespaceName(namespace);
    Set<String> watchedKeys = watchKeysUtil.assembleAllWatchKeys(appId, cluster, namespace, dataCenter);
    DeferredResult<ResponseEntity<ApolloConfigNotification>> deferredResult = new DeferredResult<>(TIMEOUT, NOT_MODIFIED_RESPONSE);
    // check whether client is out-dated
    ReleaseMessage latest = releaseMessageService.findLatestReleaseMessageForMessages(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();
    if (latest != null && latest.getId() != notificationId) {
        deferredResult.setResult(new ResponseEntity<>(new ApolloConfigNotification(namespace, latest.getId()), HttpStatus.OK));
    } else {
        // register all keys
        for (String key : watchedKeys) {
            this.deferredResults.put(key, deferredResult);
        }
        deferredResult.onTimeout(() -> logWatchedKeys(watchedKeys, "Apollo.LongPoll.TimeOutKeys"));
        deferredResult.onCompletion(() -> {
            // unregister all keys
            for (String key : watchedKeys) {
                deferredResults.remove(key, deferredResult);
            }
            logWatchedKeys(watchedKeys, "Apollo.LongPoll.CompletedKeys");
        });
        logWatchedKeys(watchedKeys, "Apollo.LongPoll.RegisteredKeys");
        logger.debug("Listening {} from appId: {}, cluster: {}, namespace: {}, datacenter: {}", watchedKeys, appId, cluster, namespace, dataCenter);
    }
    return deferredResult;
}
Also used : ResponseEntity(org.springframework.http.ResponseEntity) ReleaseMessage(com.ctrip.framework.apollo.biz.entity.ReleaseMessage) ApolloConfigNotification(com.ctrip.framework.apollo.core.dto.ApolloConfigNotification) DeferredResult(org.springframework.web.context.request.async.DeferredResult) GetMapping(org.springframework.web.bind.annotation.GetMapping)

Example 35 with ApolloConfigNotification

use of com.ctrip.framework.apollo.core.dto.ApolloConfigNotification in project apollo by ctripcorp.

the class NotificationControllerV2Test method testPollNotificationWithIncorrectCase.

@Test
public void testPollNotificationWithIncorrectCase() throws Exception {
    String appIdWithIncorrectCase = someAppId.toUpperCase();
    String namespaceWithIncorrectCase = defaultNamespace.toUpperCase();
    String someMessage = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).join(someAppId, someCluster, defaultNamespace);
    String someWatchKey = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).join(appIdWithIncorrectCase, someCluster, defaultNamespace);
    Multimap<String, String> watchKeysMap = assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey));
    String notificationAsString = transformApolloConfigNotificationsToString(defaultNamespace.toUpperCase(), someNotificationId);
    when(namespaceUtil.filterNamespaceName(namespaceWithIncorrectCase)).thenReturn(namespaceWithIncorrectCase);
    when(namespaceUtil.normalizeNamespace(appIdWithIncorrectCase, namespaceWithIncorrectCase)).thenReturn(defaultNamespace);
    when(watchKeysUtil.assembleAllWatchKeys(appIdWithIncorrectCase, someCluster, Sets.newHashSet(defaultNamespace), someDataCenter)).thenReturn(watchKeysMap);
    DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> deferredResult = controller.pollNotification(appIdWithIncorrectCase, someCluster, notificationAsString, someDataCenter, someClientIp);
    long someId = 1;
    ReleaseMessage someReleaseMessage = new ReleaseMessage(someMessage);
    someReleaseMessage.setId(someId);
    controller.handleMessage(someReleaseMessage, Topics.APOLLO_RELEASE_TOPIC);
    assertTrue(deferredResult.hasResult());
    ResponseEntity<List<ApolloConfigNotification>> response = (ResponseEntity<List<ApolloConfigNotification>>) deferredResult.getResult();
    assertEquals(1, response.getBody().size());
    ApolloConfigNotification notification = response.getBody().get(0);
    assertEquals(HttpStatus.OK, response.getStatusCode());
    assertEquals(namespaceWithIncorrectCase, notification.getNamespaceName());
    assertEquals(someId, notification.getNotificationId());
    ApolloNotificationMessages notificationMessages = notification.getMessages();
    assertEquals(1, notificationMessages.getDetails().size());
    assertEquals(someId, notificationMessages.get(someMessage).longValue());
}
Also used : ResponseEntity(org.springframework.http.ResponseEntity) ReleaseMessage(com.ctrip.framework.apollo.biz.entity.ReleaseMessage) ApolloNotificationMessages(com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages) ApolloConfigNotification(com.ctrip.framework.apollo.core.dto.ApolloConfigNotification) List(java.util.List) Test(org.junit.Test)

Aggregations

ApolloConfigNotification (com.ctrip.framework.apollo.core.dto.ApolloConfigNotification)53 Test (org.junit.Test)41 ApolloNotificationMessages (com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages)28 Sql (org.springframework.test.context.jdbc.Sql)26 List (java.util.List)20 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)15 ReleaseMessage (com.ctrip.framework.apollo.biz.entity.ReleaseMessage)9 ResponseEntity (org.springframework.http.ResponseEntity)8 HttpRequest (com.ctrip.framework.apollo.util.http.HttpRequest)6 InvocationOnMock (org.mockito.invocation.InvocationOnMock)6 ServiceDTO (com.ctrip.framework.apollo.core.dto.ServiceDTO)5 HttpResponse (com.ctrip.framework.apollo.util.http.HttpResponse)5 ApolloConfig (com.ctrip.framework.apollo.core.dto.ApolloConfig)4 BaseIntegrationTest (com.ctrip.framework.apollo.BaseIntegrationTest)3 Config (com.ctrip.framework.apollo.Config)3 ConfigChangeListener (com.ctrip.framework.apollo.ConfigChangeListener)3 ConfigChangeEvent (com.ctrip.framework.apollo.model.ConfigChangeEvent)3 Map (java.util.Map)3 ContextHandler (org.eclipse.jetty.server.handler.ContextHandler)3 DeferredResultWrapper (com.ctrip.framework.apollo.configservice.wrapper.DeferredResultWrapper)2