示例#1
0
    @Override
    public State<?, ?> handle(
        ClusterContext context, Message<ClusterMessage> message, MessageHolder outgoing)
        throws Throwable {
      List<URI> discoveredInstances = context.getDiscoveredInstances();
      switch (message.getMessageType()) {
        case configurationResponse:
          {
            context.timeouts.cancelTimeout("discovery");

            ClusterMessage.ConfigurationResponseState state = message.getPayload();

            context.getLogger(ClusterState.class).info("Joining cluster " + state.getClusterName());
            if (!context.getConfiguration().getName().equals(state.getClusterName())) {
              context
                  .getLogger(ClusterState.class)
                  .warn(
                      "Joined cluster name is different than "
                          + "the one configured. Expected "
                          + context.getConfiguration().getName()
                          + ", got "
                          + state.getClusterName()
                          + ".");
            }

            List<URI> memberList = new ArrayList<URI>(state.getMembers());
            context.learnerContext.setLastDeliveredInstanceId(
                state.getLatestReceivedInstanceId().getId());
            context.learnerContext.learnedInstanceId(state.getLatestReceivedInstanceId().getId());
            context.proposerContext.nextInstanceId =
                state.getLatestReceivedInstanceId().getId() + 1;

            context.acquiredConfiguration(memberList, state.getRoles());

            if (!memberList.contains(context.me)) {
              context
                  .getLogger(ClusterState.class)
                  .info(
                      String.format(
                          "%s joining:%s, " + "last delivered:%d",
                          context.me.toString(),
                          context.getConfiguration().toString(),
                          state.getLatestReceivedInstanceId().getId()));

              ClusterMessage.ConfigurationChangeState newState =
                  new ClusterMessage.ConfigurationChangeState();
              newState.join(context.me);

              // Let the coordinator propose this if possible
              URI coordinator = state.getRoles().get(ClusterConfiguration.COORDINATOR);
              if (coordinator != null) {
                outgoing.offer(to(ProposerMessage.propose, coordinator, newState));
              } else {
                outgoing.offer(
                    to(
                        ProposerMessage.propose,
                        new URI(message.getHeader(Message.FROM)),
                        newState));
              }

              context
                  .getLogger(ClusterState.class)
                  .debug("Setup join timeout for " + message.getHeader(Message.CONVERSATION_ID));
              context.timeouts.setTimeout(
                  "join",
                  timeout(
                      ClusterMessage.joiningTimeout,
                      message,
                      new URI(message.getHeader(Message.FROM))));

              return joining;
            } else {
              // Already in (probably due to crash of this server previously), go to entered state
              context.joined();
              outgoing.offer(internal(ClusterMessage.joinResponse, context.getConfiguration()));

              return entered;
            }
          }

        case configurationTimeout:
          {
            ClusterMessage.ConfigurationTimeoutState state = message.getPayload();
            if (state.getRemainingPings() > 0) {
              // Send out requests again
              for (URI potentialClusterInstanceUri : context.getJoiningInstances()) {
                outgoing.offer(
                    to(ClusterMessage.configurationRequest, potentialClusterInstanceUri));
              }
              context.timeouts.setTimeout(
                  "join",
                  timeout(
                      ClusterMessage.configurationTimeout,
                      message,
                      new ClusterMessage.ConfigurationTimeoutState(state.getRemainingPings() - 1)));
            } else {
              // No responses
              // Check if we picked up any other instances' requests during this phase
              if (!discoveredInstances.isEmpty()) {
                Collections.sort(discoveredInstances);
                /*
                 * The assumption here is that the lowest in the list of discovered instances
                 * will create the cluster. Keep in mind that this is run on all instances so
                 * everyone will pick the same one.
                 * If the one picked up is configured to not init a cluster then the timeout
                 * set in else{} will take care of that.
                 */
                if (discoveredInstances.get(0).compareTo(context.getMe()) >= 0) {
                  discoveredInstances.clear();

                  // I'm supposed to create the cluster - fail the join
                  outgoing.offer(
                      internal(
                          ClusterMessage.joinFailure,
                          new TimeoutException("Join failed, timeout waiting for configuration")));
                  return start;
                } else {
                  discoveredInstances.clear();

                  // Someone else is supposed to create the cluster - restart the join discovery
                  for (URI potentialClusterInstanceUri : context.getJoiningInstances()) {
                    outgoing.offer(
                        to(ClusterMessage.configurationRequest, potentialClusterInstanceUri));
                  }
                  context.timeouts.setTimeout(
                      "discovery",
                      timeout(
                          ClusterMessage.configurationTimeout,
                          message,
                          new ClusterMessage.ConfigurationTimeoutState(4)));
                }
              } else {
                // Join failed
                outgoing.offer(
                    internal(
                        ClusterMessage.joinFailure,
                        new TimeoutException("Join failed, timeout waiting for configuration")));
                return start;
              }
            }

            return this;
          }

        case configurationRequest:
          {
            // We're listening for existing clusters, but if all instances start up at the same time
            // and look for each other, this allows us to pick that up
            URI joiningInstanceUri = new URI(message.getHeader(Message.FROM));
            if (!discoveredInstances.contains(joiningInstanceUri)) {
              discoveredInstances.add(joiningInstanceUri);
            }
          }
      }

      return this;
    }