com.netflix.zuul.origins.BasicNettyOrigin Maven / Gradle / Ivy
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.zuul.origins;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.config.CachedDynamicBooleanProperty;
import com.netflix.config.CachedDynamicIntProperty;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.Registry;
import com.netflix.zuul.context.CommonContextKeys;
import com.netflix.zuul.context.SessionContext;
import com.netflix.zuul.discovery.DiscoveryResult;
import com.netflix.zuul.exception.ErrorType;
import com.netflix.zuul.message.http.HttpRequestMessage;
import com.netflix.zuul.message.http.HttpResponseMessage;
import com.netflix.zuul.netty.NettyRequestAttemptFactory;
import com.netflix.zuul.netty.SpectatorUtils;
import com.netflix.zuul.netty.connectionpool.ClientChannelManager;
import com.netflix.zuul.netty.connectionpool.DefaultClientChannelManager;
import com.netflix.zuul.netty.connectionpool.PooledConnection;
import com.netflix.zuul.niws.RequestAttempt;
import com.netflix.zuul.passport.CurrentPassport;
import com.netflix.zuul.stats.status.StatusCategory;
import com.netflix.zuul.stats.status.StatusCategoryUtils;
import com.netflix.zuul.stats.status.ZuulStatusCategory;
import io.netty.channel.EventLoop;
import io.netty.util.concurrent.Promise;
import java.net.InetAddress;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* Netty Origin basic implementation that can be used for most apps, with the more complex methods having no-op
* implementations.
*
* Author: Arthur Gonigberg
* Date: December 01, 2017
*/
public class BasicNettyOrigin implements NettyOrigin {
private final OriginName originName;
private final Registry registry;
private final IClientConfig config;
private final ClientChannelManager clientChannelManager;
private final NettyRequestAttemptFactory requestAttemptFactory;
private final AtomicInteger concurrentRequests;
private final Counter rejectedRequests;
private final CachedDynamicIntProperty concurrencyMax;
private final CachedDynamicBooleanProperty concurrencyProtectionEnabled;
public BasicNettyOrigin(OriginName originName, Registry registry) {
this.originName = Objects.requireNonNull(originName, "originName");
this.registry = registry;
this.config = setupClientConfig(originName);
this.clientChannelManager = new DefaultClientChannelManager(originName, config, registry);
this.clientChannelManager.init();
this.requestAttemptFactory = new NettyRequestAttemptFactory();
String niwsClientName = getName().getNiwsClientName();
this.concurrentRequests =
SpectatorUtils.newGauge("zuul.origin.concurrent.requests", niwsClientName, new AtomicInteger(0));
this.rejectedRequests = SpectatorUtils.newCounter("zuul.origin.rejected.requests", niwsClientName);
this.concurrencyMax =
new CachedDynamicIntProperty("zuul.origin." + niwsClientName + ".concurrency.max.requests", 200);
this.concurrencyProtectionEnabled = new CachedDynamicBooleanProperty(
"zuul.origin." + niwsClientName + ".concurrency.protect.enabled", true);
}
protected IClientConfig setupClientConfig(OriginName originName) {
// Get the NIWS properties for this Origin.
IClientConfig niwsClientConfig =
DefaultClientConfigImpl.getClientConfigWithDefaultValues(originName.getNiwsClientName());
niwsClientConfig.set(CommonClientConfigKey.ClientClassName, originName.getNiwsClientName());
niwsClientConfig.loadProperties(originName.getNiwsClientName());
return niwsClientConfig;
}
@Override
public OriginName getName() {
return originName;
}
@Override
public boolean isAvailable() {
return clientChannelManager.isAvailable();
}
@Override
public boolean isCold() {
return clientChannelManager.isCold();
}
@Override
public Promise connectToOrigin(
HttpRequestMessage zuulReq,
EventLoop eventLoop,
int attemptNumber,
CurrentPassport passport,
AtomicReference chosenServer,
AtomicReference super InetAddress> chosenHostAddr) {
return clientChannelManager.acquire(eventLoop, null, passport, chosenServer, chosenHostAddr);
}
@Override
public int getMaxRetriesForRequest(SessionContext context) {
return config.get(CommonClientConfigKey.MaxAutoRetriesNextServer, 0);
}
@Override
public RequestAttempt newRequestAttempt(
DiscoveryResult server, InetAddress serverAddr, SessionContext zuulCtx, int attemptNum) {
return new RequestAttempt(
server, serverAddr, config, attemptNum, config.get(CommonClientConfigKey.ReadTimeout));
}
@Override
public String getIpAddrFromServer(DiscoveryResult discoveryResult) {
final Optional ipAddr = discoveryResult.getIPAddr();
return ipAddr.isPresent() ? ipAddr.get() : null;
}
@Override
public IClientConfig getClientConfig() {
return config;
}
@Override
public Registry getSpectatorRegistry() {
return registry;
}
@Override
public void recordFinalError(HttpRequestMessage requestMsg, Throwable throwable) {
if (throwable == null) {
return;
}
final SessionContext zuulCtx = requestMsg.getContext();
// Choose StatusCategory based on the ErrorType.
final ErrorType et = requestAttemptFactory.mapNettyToOutboundErrorType(throwable);
final StatusCategory nfs = et.getStatusCategory();
StatusCategoryUtils.setStatusCategory(zuulCtx, nfs);
StatusCategoryUtils.setOriginStatusCategory(zuulCtx, nfs);
zuulCtx.setError(throwable);
}
@Override
public void recordFinalResponse(HttpResponseMessage resp) {
if (resp != null) {
final SessionContext zuulCtx = resp.getContext();
// Store the status code of final attempt response.
int originStatusCode = resp.getStatus();
zuulCtx.put(CommonContextKeys.ORIGIN_STATUS, originStatusCode);
// Mark origin StatusCategory based on http status code.
StatusCategory originNfs = ZuulStatusCategory.SUCCESS;
if (originStatusCode == 503) {
originNfs = ZuulStatusCategory.FAILURE_ORIGIN_THROTTLED;
} else if (StatusCategoryUtils.isResponseHttpErrorStatus(originStatusCode)) {
originNfs = ZuulStatusCategory.FAILURE_ORIGIN;
}
StatusCategoryUtils.setOriginStatusCategory(zuulCtx, originNfs);
// Choose the zuul StatusCategory based on the origin one...
// ... but only if existing one has not already been set to a non-success value.
StatusCategoryUtils.storeStatusCategoryIfNotAlreadyFailure(zuulCtx, originNfs);
}
}
@Override
public void preRequestChecks(HttpRequestMessage zuulRequest) {
if (concurrencyProtectionEnabled.get() && concurrentRequests.get() > concurrencyMax.get()) {
rejectedRequests.increment();
throw new OriginConcurrencyExceededException(getName());
}
concurrentRequests.incrementAndGet();
}
@Override
public void recordProxyRequestEnd() {
concurrentRequests.decrementAndGet();
}
/* Not required for basic operation */
@Override
public double getErrorPercentage() {
return 0;
}
@Override
public double getErrorAllPercentage() {
return 0;
}
@Override
public void onRequestExecutionStart(HttpRequestMessage zuulReq) {}
@Override
public void onRequestStartWithServer(HttpRequestMessage zuulReq, DiscoveryResult discoveryResult, int attemptNum) {}
@Override
public void onRequestExceptionWithServer(
HttpRequestMessage zuulReq, DiscoveryResult discoveryResult, int attemptNum, Throwable t) {}
@Override
public void onRequestExecutionSuccess(
HttpRequestMessage zuulReq,
HttpResponseMessage zuulResp,
DiscoveryResult discoveryResult,
int attemptNum) {}
@Override
public void onRequestExecutionFailed(
HttpRequestMessage zuulReq, DiscoveryResult discoveryResult, int attemptNum, Throwable t) {}
@Override
public void adjustRetryPolicyIfNeeded(HttpRequestMessage zuulRequest) {}
@Override
public void recordSuccessResponse() {}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy