private AbstractEventLoopState state(SessionProtocol sessionProtocol,
                                         EndpointGroup endpointGroup,
                                         @Nullable Endpoint endpoint) {
        if (endpoint == null) {
            // Use a fake endpoint if no endpoint was selected from the endpointGroup.
            endpoint = Endpoint.unsafeCreate(
                    "armeria-group-" + Integer.toHexString(System.identityHashCode(endpointGroup)), 0);
        }

        final String firstTryHost;
        final String secondTryHost;
        if (endpoint.hasIpAddr()) {
            final String ipAddr = endpoint.ipAddr();
            assert ipAddr != null;
            firstTryHost = ipAddr;
            if (endpoint.isIpAddrOnly()) {
                secondTryHost = null;
            } else {
                secondTryHost = endpoint.host();
            }
        } else {
            firstTryHost = endpoint.host();
            secondTryHost = null;
        }

        final Endpoint endpointWithPort = endpoint.withDefaultPort(sessionProtocol);
        final int port = endpointWithPort.port();
        final boolean isHttp1 = isHttp1(sessionProtocol, endpointWithPort);
        final StateKey firstKey = new StateKey(firstTryHost, port, isHttp1);
        AbstractEventLoopState state = states.get(firstKey);
        if (state != null) {
            return state;
        }

        if (secondTryHost != null) {
            final StateKey secondKey = new StateKey(secondTryHost, port, isHttp1);
            state = states.get(secondKey);
            if (state != null) {
                return state;
            }
        }

        // Try with the endpoint which has a port first.
        int maxNumEventLoopsCandidate = maxNumEventLoopsCandidate(endpointWithPort);
        if (maxNumEventLoopsCandidate <= 0 && !endpointWithPort.equals(endpoint)) {
            // Try without the port second.
            maxNumEventLoopsCandidate = maxNumEventLoopsCandidate(endpoint);
        }

        final int maxNumEventLoops =
                maxNumEventLoopsCandidate > 0 ? Math.min(maxNumEventLoopsCandidate, eventLoops.size())
                                              : maxNumEventLoops(sessionProtocol, endpointWithPort);
        return states.computeIfAbsent(firstKey,
                                      unused -> AbstractEventLoopState.of(eventLoops, maxNumEventLoops, this));
    }
Returns the {@link AbstractEventLoopState} in the map whose key is {@link StateKey}. If the IP address
exists in the specified {@link Endpoint}, it's used in the key to find the
{@link AbstractEventLoopState} first. Then {@link Endpoint#host()} is used.
If {@link AbstractEventLoopState} does not exist in the map, the state is created with the maximum
number of {@link EventLoop}s. The number can be produced by the {@code maxNumEventLoopsFunction} or
{@code maxNumEventLoopsPerEndpoint} and {@code maxNumEventLoopsPerHttp1Endpoint} dependent on
the {@link SessionProtocol} when the {@code maxNumEventLoopsFunction} does not produce a value.
core/src/main/java/com/linecorp/armeria/client/DefaultEventLoopScheduler.java