Code Monkey home page Code Monkey logo

ribbon's Introduction

Ribbon

Ribbon is a client side IPC library that is battle-tested in cloud. It provides the following features

  • Load balancing
  • Fault tolerance
  • Multiple protocol (HTTP, TCP, UDP) support in an asynchronous and reactive model
  • Caching and batching

To get ribbon binaries, go to maven central. Here is an example to add dependency in Maven:

<dependency>
    <groupId>com.netflix.ribbon</groupId>
    <artifactId>ribbon</artifactId>
    <version>2.2.2</version>
</dependency>

Modules

  • ribbon: APIs that integrate load balancing, fault tolerance, caching/batching on top of other ribbon modules and Hystrix
  • ribbon-loadbalancer: Load balancer APIs that can be used independently or with other modules
  • ribbon-eureka: APIs using Eureka client to provide dynamic server list for cloud
  • ribbon-transport: Transport clients that support HTTP, TCP and UDP protocols using RxNetty with load balancing capability
  • ribbon-httpclient: REST client built on top of Apache HttpClient integrated with load balancers (deprecated and being replaced by ribbon module)
  • ribbon-example: Examples
  • ribbon-core: Client configuration APIs and other shared APIs

Project Status: On Maintenance

Ribbon comprises of multiple components some of which are used in production internally and some of which were replaced by non-OSS solutions over time. This is because Netflix started moving into a more componentized architecture for RPC with a focus on single-responsibility modules. So each Ribbon component gets a different level of attention at this moment.

More specifically, here are the components of Ribbon and their level of attention by our teams:

  • ribbon-core: deployed at scale in production
  • ribbon-eureka: deployed at scale in production
  • ribbon-evcache: not used
  • ribbon-guice: not used
  • ribbon-httpclient: we use everything not under com.netflix.http4.ssl. Instead, we use an internal solution developed by our cloud security team
  • ribbon-loadbalancer: deployed at scale in production
  • ribbon-test: this is just an internal integration test suite
  • ribbon-transport: not used
  • ribbon: not used

Even for the components deployed in production we have wrapped them in a Netflix internal http client and we are not adding new functionality since they’ve been stable for a while. Any new functionality has been added to internal wrappers on top of Ribbon (such as request tracing and metrics). We have not made an effort to make those components Netflix-agnostic under Ribbon.

Recognizing these realities and deficiencies, we are placing Ribbon in maintenance mode. This means that if an external user submits a large feature request, internally we wouldn’t prioritize it highly. However, if someone were to do work on their own and submit complete pull requests, we’d be happy to review and accept. Our team has instead started building an RPC solution on top of gRPC. We are doing this transition for two main reasons: multi-language support and better extensibility/composability through request interceptors. That’s our current plan moving forward.

We currently contribute to the gRPC code base regularly. To help our teams migrate to a gRPC-based solution in production (and battle-test it), we are also adding load-balancing and discovery interceptors to achieve feature parity with the functionality Ribbon and Eureka provide. The interceptors are Netflix-internal at the moment. When we reach that level of confidence we hope to open-source this new approach. We don’t expect this to happen before Q3 of 2016.

Release notes

See https://github.com/Netflix/ribbon/releases

Code example

Access HTTP resource using template (full example)

HttpResourceGroup httpResourceGroup = Ribbon.createHttpResourceGroup("movieServiceClient",
            ClientOptions.create()
                    .withMaxAutoRetriesNextServer(3)
                    .withConfigurationBasedServerList("localhost:8080,localhost:8088"));
HttpRequestTemplate<ByteBuf> recommendationsByUserIdTemplate = httpResourceGroup.newTemplateBuilder("recommendationsByUserId", ByteBuf.class)
            .withMethod("GET")
            .withUriTemplate("/users/{userId}/recommendations")
            .withFallbackProvider(new RecommendationServiceFallbackHandler())
            .withResponseValidator(new RecommendationServiceResponseValidator())
            .build();
Observable<ByteBuf> result = recommendationsByUserIdTemplate.requestBuilder()
                        .withRequestProperty("userId", "user1")
                        .build()
                        .observe();

Access HTTP resource using annotations (full example)

public interface MovieService {
    @Http(
            method = HttpMethod.GET,
            uri = "/users/{userId}/recommendations",
            )
    RibbonRequest<ByteBuf> recommendationsByUserId(@Var("userId") String userId);
}

MovieService movieService = Ribbon.from(MovieService.class);
Observable<ByteBuf> result = movieService.recommendationsByUserId("user1").observe();

Create an AWS-ready load balancer with Eureka dynamic server list and zone affinity enabled

        IRule rule = new AvailabilityFilteringRule();
        ServerList<DiscoveryEnabledServer> list = new DiscoveryEnabledNIWSServerList("MyVIP:7001");
        ServerListFilter<DiscoveryEnabledServer> filter = new ZoneAffinityServerListFilter<DiscoveryEnabledServer>();
        ZoneAwareLoadBalancer<DiscoveryEnabledServer> lb = LoadBalancerBuilder.<DiscoveryEnabledServer>newBuilder()
                .withDynamicServerList(list)
                .withRule(rule)
                .withServerListFilter(filter)
                .buildDynamicServerListLoadBalancer();   
        DiscoveryEnabledServer server = lb.chooseServer();         

Use LoadBalancerCommand to load balancing IPC calls made by HttpURLConnection (full example)

CommandBuilder.<String>newBuilder()
        .withLoadBalancer(LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList))
        .build(new LoadBalancerExecutable<String>() {
            @Override
            public String run(Server server) throws Exception {
                URL url = new URL("http://" + server.getHost() + ":" + server.getPort() + path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                return conn.getResponseMessage();
            }
        }).execute();

License

Copyright 2014 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.

Questions?

Email [email protected] or join us

ribbon's People

Contributors

aspyker avatar brharrington avatar capt-planet avatar carl-mastrangelo avatar casidiablo avatar diptanu avatar dmitry-cherkas avatar drtechniko avatar elandau avatar jdamick avatar kerumai avatar leogomes avatar lowzj avatar mattnelson avatar mikeycohen avatar powerlambda avatar qiangdavidliu avatar raksoras avatar ravilestm avatar rk13 avatar rpalcolea avatar rspieldenner avatar sghill avatar stonse avatar sullis avatar tcellucci avatar timmc-bcov avatar twicksell avatar willblackie avatar zarfide avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ribbon's Issues

Bug/Problem in RondRobinRule

If I have a server list based on custom configuration and one of the servers is down, the LB is getting a connection timeout and retries. That's good.

However, after the number is retries is exhausted the LB doesn't go to the second server it returns an error to the Client. I would expect it to do a failover and start talking to the other server.

Possibly to configure Jersey client while also using ClientFactory?

I'm trying to use ribbon to send some JSON to a backend service. If I manually configure the Jersey client and enable POJO mapping, things work fine. However, if I get my RestClient from ClientFactory, the POJO mapping doesn't seem to work quite right.

It would be nice if we could get access to the Jersey ClientConfig before the Jersey Client is initialized.

E.g.,

RestClient client = (RestClient) ClientFactory.getNamedClient("solr-client", new ExtraJerseyConfig(){
    @Override
    public void configure(ClientConfig config) {
      config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
    }
});

Another option would be to add two maps to the client factory methods (Map<String, Boolean> for features, Map<String, Object> for properties) that then get copied onto the ClientConfig.

Or, you could just add POJO mapping as a ribbon config option :)

New async interfaces

It seems that the current async interfaces do not allow flexible and reusable decoders for Netty. The coupling of streaming based interface and request/response based interface also makes it confusing.

Here is the new proposed async interfaces for simple request/response style communication:

public interface AsyncClient<T extends ClientRequest, S extends IResponse> extends Closeable {
    /**
     * @param type Object to hold the runtime type of the expected entity to be used by entity decoder/deserializer
     * @param <E> Type of the expected entity, can also be type of IResponse to indicate that the caller is interested to
     *           receive the callback on the raw response, or Void to indicate that the caller is not interested to receive
     *           any response from the server
     */
    public <E> ListenableFuture<E> execute(T request, TypeDef<E> type);
}

public interface ListenableFuture<T> extends Future<T> {
    public void addListener(ResponseCallback<T> callback);
}

public interface ResponseCallback<T> {

    public void completed(T result);

    public void failed(Throwable e);

    public void cancelled();
}

Specifically for HTTP:

public interface HttpResponse extends IResponse, Closeable {
    public int getStatus();

    public Status getStatusMessage();

    @Deprecated
    public <T> T getEntity(TypeToken<T> type) throws Exception;

    @Deprecated
    public <T> T getEntity(Class<T> type) throws Exception;

    public InputStream getInputStream();

    public HttpHeaders getHttpHeaders();

    public void close();    
}

public interface AsyncHttpClient extends AsyncClient<HttpRequest, HttpResponse> {
}

Here is the interface for streaming client:

public interface AsyncStreamingClient<T extends ClientRequest, START extends IResponse, END extends IResponse, E> extends Closeable {
    public ListenableStreamingFuture<START, END, E> execute(T request, StreamingResponseCallback<START, END, E> callback, TypeToken<E> type);
}

public interface ListenableStreamingFuture<START extends IResponse, END extends IResponse, T> extends Future<END> {
    public void addListener(StreamingResponseCallback<START, END, T> callback);
}

public interface StreamingResponseCallback<START extends IResponse, END extends IResponse, T> extends ResponseCallback<END>{    

    public void started(START start);

    public void contentReceived(T obj);
}

Here the streaming client is generically typed for the entity it expects to receive.

LoadBalancerContext.computeFinalUriWithLoadBalancer can double encode a URI

I'm working with Ribbon to contact Jenkins to download artifacts. I'm also playing around with artifact names that would need to be escaped such as test<script>alert(42) and test foo bar.txt.

When building up the URI to send in to Ribbon, I use a URIBuilder with the .segment() and .path() methods and then I call .build() to get the actual URI which will escape values. For example, test<script>alert(42) would become test%3Cscript%3Ealert(42) and I end up with a partial URI like the following:

/job/8487c345-f520-269f-3074-75f206dade6b/job/7431934f-155f-45de-b19d-66493453a317/1/artifact/test%3Cscript%3Ealert(42)

The problem is that Ribbon will then take that URI and piece it together with the host/port information with a StringBuilder in computeFinalUriWithLoadBalancer and then create a new URI with the value from the StringBuilder. This ends up double encoding parts of the URI so that we get

/job/8487c345-f520-269f-3074-75f206dade6b/job/7431934f-155f-45de-b19d-66493453a317/1/artifact/test%253Cscript%253Ealert(42)

Once that's done, the URI is no longer valid and I end up getting 404 errors.

I also tried sending just a string URI to Ribbon that looked like

/job/8487c345-f520-269f-3074-75f206dade6b/job/7431934f-155f-45de-b19d-66493453a317/1/artifact/test<script>alert(42)

Ribbon just takes strings and tries to create a URI, and since there are illegal characters in the string, it fails. Basically, we end up with two options: either we get a double encoded string or we don't get any encoding at all and Ribbon fails when trying to create the URI.

NoClassDefFoundError on Jersey ServletContainer

I get this error when trying to include com.netflix.ribbon:ribbon-eureka:0.3.8 or above in a pytheas project. 0.3.7 or below is okay:

java.lang.NoClassDefFoundError: com/sun/jersey/spi/container/servlet/ServletContainer
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:392)
    at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
    at com.netflix.explorers.karyon.ExplorerKaryonServerBootstrap$1.configureServlets(ExplorerKaryonServerBootstrap.java:101)
    at com.google.inject.servlet.ServletModule.configure(ServletModule.java:53)
    at com.google.inject.AbstractModule.configure(AbstractModule.java:59)
    at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:223)
    at com.google.inject.spi.Elements.getElements(Elements.java:101)
    at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:133)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:103)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:217)
    at com.netflix.governator.guice.LifecycleInjector.createChildInjector(LifecycleInjector.java:140)
    at com.netflix.governator.guice.LifecycleInjector.createInjector(LifecycleInjector.java:185)
    at com.netflix.governator.guice.LifecycleInjector.createInjector(LifecycleInjector.java:150)
    at com.netflix.karyon.server.ServerBootstrap.createInjector(ServerBootstrap.java:188)
    at com.netflix.karyon.server.ServerBootstrap.bootstrap(ServerBootstrap.java:127)
    at com.netflix.karyon.server.KaryonServer.initialize(KaryonServer.java:182)
    at com.netflix.karyon.server.guice.KaryonGuiceContextListener.getInjector(KaryonGuiceContextListener.java:67)
    at com.google.inject.servlet.GuiceServletContextListener.contextInitialized(GuiceServletContextListener.java:45)
    at com.netflix.karyon.server.guice.KaryonGuiceContextListener.contextInitialized(KaryonGuiceContextListener.java:72)
    at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:548)
    at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
    at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1272)
    at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517)
    at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:489)
    at org.gradle.api.plugins.jetty.internal.JettyPluginWebAppContext.doStart(JettyPluginWebAppContext.java:112)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerCollection.doStart(HandlerCollection.java:152)
    at org.mortbay.jetty.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:156)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerCollection.doStart(HandlerCollection.java:152)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
    at org.mortbay.jetty.Server.doStart(Server.java:224)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.gradle.api.plugins.jetty.internal.Jetty6PluginServer.start(Jetty6PluginServer.java:111)
    at org.gradle.api.plugins.jetty.AbstractJettyRunTask.startJettyInternal(AbstractJettyRunTask.java:247)
    at org.gradle.api.plugins.jetty.AbstractJettyRunTask.startJetty(AbstractJettyRunTask.java:198)
    at org.gradle.api.plugins.jetty.AbstractJettyRunTask.start(AbstractJettyRunTask.java:169)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1047)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:877)
    at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:196)
    at org.gradle.api.internal.BeanDynamicObject.invokeMethod(BeanDynamicObject.java:102)
    at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:99)
    at org.gradle.api.plugins.jetty.JettyRun_Decorated.invokeMethod(Unknown Source)
    at groovy.lang.GroovyObject$invokeMethod.call(Unknown Source)
    at org.gradle.util.ReflectionUtil.invoke(ReflectionUtil.groovy:23)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$4.execute(AnnotationProcessingTaskFactory.java:150)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$4.execute(AnnotationProcessingTaskFactory.java:145)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:472)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:461)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:60)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:34)
    at org.gradle.api.internal.changedetection.CacheLockHandlingTaskExecuter$1.run(CacheLockHandlingTaskExecuter.java:34)
    at org.gradle.cache.internal.DefaultCacheAccess$2.create(DefaultCacheAccess.java:200)
    at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:172)
    at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:198)
    at org.gradle.cache.internal.DefaultPersistentDirectoryStore.longRunningOperation(DefaultPersistentDirectoryStore.java:137)
    at org.gradle.api.internal.changedetection.DefaultTaskArtifactStateCacheAccess.longRunningOperation(DefaultTaskArtifactStateCacheAccess.java:83)
    at org.gradle.api.internal.changedetection.CacheLockHandlingTaskExecuter.execute(CacheLockHandlingTaskExecuter.java:32)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:41)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:42)
    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:247)
    at org.gradle.execution.DefaultTaskGraphExecuter.executeTask(DefaultTaskGraphExecuter.java:192)
    at org.gradle.execution.DefaultTaskGraphExecuter.doExecute(DefaultTaskGraphExecuter.java:177)
    at org.gradle.execution.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:83)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:36)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
    at org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter$1.run(TaskCacheLockHandlingBuildExecuter.java:31)
    at org.gradle.cache.internal.DefaultCacheAccess$1.create(DefaultCacheAccess.java:111)
    at org.gradle.cache.internal.DefaultCacheAccess.useCache(DefaultCacheAccess.java:126)
    at org.gradle.cache.internal.DefaultCacheAccess.useCache(DefaultCacheAccess.java:109)
    at org.gradle.cache.internal.DefaultPersistentDirectoryStore.useCache(DefaultPersistentDirectoryStore.java:129)
    at org.gradle.api.internal.changedetection.DefaultTaskArtifactStateCacheAccess.useCache(DefaultTaskArtifactStateCacheAccess.java:79)
    at org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter.execute(TaskCacheLockHandlingBuildExecuter.java:29)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:54)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:155)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:110)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:78)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:38)
    at org.gradle.launcher.exec.InProcessGradleLauncherActionExecuter.execute(InProcessGradleLauncherActionExecuter.java:39)
    at org.gradle.launcher.exec.InProcessGradleLauncherActionExecuter.execute(InProcessGradleLauncherActionExecuter.java:25)
    at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:50)
    at org.gradle.launcher.cli.ActionAdapter.execute(ActionAdapter.java:30)
    at org.gradle.launcher.cli.ActionAdapter.execute(ActionAdapter.java:22)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:200)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:173)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:138)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
    at org.gradle.launcher.Main.doAction(Main.java:48)
    at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
    at org.gradle.launcher.Main.main(Main.java:39)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:50)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:32)
    at org.gradle.launcher.GradleMain.main(GradleMain.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:33)
    at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:130)
    at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:47)
Caused by: java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:401)
    at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
    ... 134 more

Add load balancer rule that chooses the server with lowest concurrent request

The rule should skip servers with tripped circuit breaker and pick the server with the lowest concurrent requests.

This rule should typically work with SeverListSubsetFilter which puts a limit on the servers that is visible to the rule. This ensure that it only needs to find the minimal concurrent requests among a small number of servers. Also, each client will get a random list of servers which avoids the problem that one server with the lowest concurrent requests is chosen by a large number of clients and immediately gets overwhelmed.

IClientConfig should provide APIs to support arbitrary typed values

Currently IClientConfig only supports storing and retrieving configuration values as String, and a few convenience APIs to convert them to limited types like Integer and Boolean. It requires user to know the actual data type associated with each config key, and pays a conversion cost when the value is retrieved.

It would be nice for IClientConfig and DefaultClientConfigImpl to support arbitrary configuration values in an intuitive way.

Here is the proposal:

  • Associate a generic type for each common configuration key.
  • Add new APIs in IClientConfig to support retrieving values with the generically typed configuration key.
  • Reserve the behavior to read configuration values as string from Archaius and convert them to desired type on demand.

Here is the code example to show desired behavior:

    public static final IClientConfigKey<Integer> ReadTimeout = ...

    int readTimeout = clientConfig.getPropertyWithType(IClientConfigKey.CommonKeys.ReadTimeout);

Support client properties as annotations

Since we support client creation using only annotations, it would be nice for client providers to specify the client properties in the same interface. This will eliminate the needs to provide a separate properties file and keep everything in one file, which simplifies application's interaction with the client.

There are a couple of things to note:

  • The property name can be directly mapped to fields in CommonClientConfigKey
  • For Archaius based client configuration, the actual value might have been overridden by a dynamic property which should be honored. We should also "export" the values from annotations to an Archaius configuration to be viewed by a property UI.

Apache HttpClient that extends AbstractLoadBalancerAwareClient

I am looking into create a LoadBalancerAwareClient that uses Apache Client.

I've found NFHttpClient but it doesn't do what I need.

Is there such an implementation?

Would you like me to add it as a patch?

Not clear from documentation what is the purpose of NFHttpClient in the first place.

Not able to encode "/" with ribbon client.

Hi,

I have a use case wherein I need to send "/" in URL as encoded value (UTF-8).

Eg.

I have a resource name "abc/xyz". Now I need to download that resource using its GET url - "http://localhost:8080/resources/abc/xyz".

But if I use above URL, I will get 404 error as "/" between abc and xyz will not be encoded by ribbon.

Now if I use encoded URL "http://localhost:8080/resources/abc%2Fxyz" and make a GET request using ribbon, then I get hit on URL "http://localhost:8080/resources/abc%252Fxyz" at server. But server should have got hit at "http://localhost:8080/resources/abc%2Fxyz".

I debugged this issue and found following facts:

This is due to a defect in “com.netflix.client.AbstractLoadBalancerAwareClient”. This class uses a method computeFinalUriWithLoadBalancer which has following behavior:

  1.   Does not encode resource name if it contains “/” like “abc/xyz”
    
  2.   Encodes again an already encoded name like “abc%2Fxyz” to result in “abc%252Fxyz”.
    

Reduce verbosity of annotations

Some ideas to reduce the verbosity:

  • Make @TemplateName optional and default is the method name
  • instead of "uriTemplate" and "cacheKeyTemplate", just say "uri" and "cacheKey"
  • @Provider inside @CacheProviders seems repetitive. Can we remove it?

Strange/broken behavior from DefaultClientConfigImpl.getClientConfigWithDefaultValues(String clientName, String nameSpace)

We encountered this in zuul where we decided to use a namespace other than ribbon.

We had a frustrating problem where FollowRedirects allways seemed to be on no matter what we set it to in the config (since it defaults to true in the ribbon code. Why?). Yet, the config was obviously being loaded since the clients were actually found and connecting properly.

Putting in some more debug statements, we found this: the config is fine when it is first loaded (and zuul registers the client), but doing a getNamedClient and printing out the values when the client is about to be used shows all of the properties reverted back to default and a set of properties peppered in with the form ..

We've traced through everything (including all of the dynamic property loading and reconstituting the keys) and it all looks like it should be working fine, but we've burned up too much time to try and figure out exactly what is going on, so we are reverting to the default namespace.

It was extremely mysterious to us as to how the system was working at all, but it seems like the load-balancer is a static config and we were mostly okay based on the initial registration.

Add IClientConfig builder

Currently programmatically construction of IClientConfig is not easy. DefaultClientConfigImpl is largely depending on loading properties from Archaius, which is a black box. Also, each property has to be set via the setProperty() method, which is not intuitive. We would like to have a builder that is capable of construction of DefaultClientConfigImpl with clear method names to indicate what it is doing. Also, important configurations should be able to be set via withXYZ method.

For example, to set read timeout, it can be coded as

    builder.withReadTimeout(1000);

which is more friendly than

    config.setProperty(CommonClientConfigKeys.ReadTimeout, 1000);

default properties are not applied

e.g. properties file contains follow section

ribbon.IsSecure=true

in the meantime RestClient is built without ssl support ( IsSecure = false as default value )

I tried to have a look into the code and didn't find the way where such behaviour is defined - but it is described in official docs.

Ribbon 1.x road map

Ribbon 1.0 will bring major new offerings to the NetflixOSS IPC stack on the client side. Here are the planned items:

Provide Netty based asynchronous clients with reactive style APIs

New clients will be built on top of the clients available in RxNetty and support HTTP and Sever-Sent-Event. The APIs will be reactive style as they are based on RxJava. Load balancing support will be available out of the box for the new clients. The new HTTP client will be supported by connection pools similar to the ones provided in Apache HttpClient. Builders will be available to build the new clients with the same IClientConfig used by RestClient.

Serialization support will not be available initially as it is still being worked out. However, it is possible to deserialize the content from the aggregated Netty ByteBuf to Java objects using JSON libraries like Jackson in user space.

Easy and full integration with load balancers for all clients

The code that integrates the load balancer with clients will be rewritten in RxJava. It will make it easy for any client, either from Ribbon or not, to integrate with Ribbon load balancers as long as they can provide either reactive style or blocking style of response (which will be automatically converted to reactive style by Ribbon). With one simple API to use, these clients can fully enjoy the benefits of load balancer: getting a server from load balancer, retrying on different servers, and giving feedback to the load balancer's server stats to make it resilient to failures and capable of smart routing.

Restructuring of packages

The most significant change is the load balancer APIs will be moved from core to a new sub module ribbon-loadbalancer.

Supporting of other clients

We will continue supporting the RestClient in ribbon-httpclient sub module. However, it is likely that new features will only be added to the Netty based asynchronous clients.

The HttpAsyncClient based client in ribbon-httpasyncclient sub module will no longer be available as we move away to Netty based asynchronous client.

Address major issues

Beyond 1.x

  • Out-of-the box and optimized serialization support for Netty based clients
  • Support more protocols like TCP and WebSocket
  • Better interoperation with Hystrix, which means making sense of Hystrix configuration for the Ribbon clients.
  • Make Ribbon clients easier to build and manage and support dependency injection
  • Create high level resource oriented template that manifests the best practices of using Ribbon clients in IPC calls

Add LoadBalancer builder

Currently construction of LoadBalancer largely depends on using reflection with class names set as configuration parameter in IClientConfig. This works well if every property is set in Archaius. But it is not friendly for constructing load balancer programmatically, with each component (Rule, Ping, ServerList) constructed separately before everything is assembled together.

For example, this would be desired code to create a Load Balancer:

        ZoneAwareLoadBalancer<DiscoveryEnabledServer> lb = LoadBalancerBuilder.<DiscoveryEnabledServer>newBuilder()
                .withDynamicServerList(new DiscoveryEnabledNIWSServerList())
                .withRule(new AvailabilityFilteringRule())
                .withServerListFilter(new ZoneAffinityServerListFilter<DiscoveryEnabledServer>())
                .buildDynamicServerListLoadBalancer();

DiscoveryEnabledNIWSServerList.getFilterImpl() is swallowing exceptions

was just tracking down an issue and noticed line 123 is swallowing the exception:

--> throw new ClientException(
ClientException.ErrorType.CONFIGURATION,
"Unable to get an instance of CommonClientConfigKey.NIWSServerListFilterClassName. Configured class:"
+ niwsClientConfig .getProperty(CommonClientConfigKey.NIWSServerListFilterClassName));

more context on the exception:

i was previously picking up archaius-0.5.3.jar which is missing the DeploymentContext.ContextKey.zone enum.

this was difficult to track down because of the above-mentioned swallow.

upgraded to 0.5.4.jar and the error went away.

Sample usage in wiki needs more explanations

We started to use ribbon from the sample code in the "Getting started" page of the wiki and we hit an unexpected behaviour : each server in the loadbalancer list could not be hit more than 50 times.
It seems that this kind of behaviour happens when the answer from the http servers is fast (we had an average response time around 3ms in our test case)

We solved the issue by adding a call to response.releaseResources()

If the typical usage is to use response.releaseResources() I suggest that this call should be listed in the "getting started" wiki page.

If that's not the case, could you explain what happened?

ClientFactory and client objects should support dependency injection

Currently Ribbon heavily depends on static ClientFactory to create objects like load balancers, RestClient, and rules. This makes it inflexible to be used in frameworks like Guice or Dagger.

I would like to add DI support for ClientFactory, as well as the objects it creates. The goal is to make it possible to inject a custom ClientFactory. For simple applications that only need one client or load balancer, it would also be possible to inject such without having to relying on a factory.

Lifecycle methods for ServerList?

I'm implementing a ServerList that uses Curator, and as such I need to properly shutdown the CuratorFramework and ServiceDiscovery instances to ensure clean termination.

With Karyon, I simply use the javax annotation @PreDestroy. What would be the recommended way of doing that here? I would like to avoid opening and closing the CuratorFramework each time getUpdatedListOfServers is called.

Thanks!

Possible concurrency issues in HttpRequestTemplate

HttpRequestTemplate builds Ribbon HttpRequest, and this code is executed concurrently by multiple client threads. This means that any change to HttpRequestTemplate must be safely published, so all threads have a consistent view of this class (Ribbon does not do any explicit synchronization for that).

We have a few issues in the current implementation:

  • some fields are non-final, and when assigned we do not have a guarantee that they will be properly visible to other threads. This can be work around like in Ribbon proxy, where after HttpRequestTemplate object construction, it is assigned to a final field, and any explicit modifications after that are not allowed.
  • there is some object state manipulation during client request execution (see below).

In the code snippet below:

@Override
public HttpRequestBuilder<T> requestBuilder() {
    if (setter == null) {
        setter = HystrixObservableCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(clientName))
                .andCommandKey(HystrixCommandKey.Factory.asKey(name()));
    ...
}

If setter field is null, it will be constructed on-fly by a client thread, with no proper synchronization.

I think we should solve both issues. The latter one is pretty easy to fix. The former one much harder and might require extraction from HttpRequestTemplate a builder class and making the former fully immutable.

Remaining tasks to be done for new Ribbon API in 2.0

  • Annotation based implementation
  • EVCache implementation
  • Rename packages and module
  • Implement handling of http header in ResourceGroup and ResourceTemplate
  • Make sure request can be executed multiple times
  • Implement copying of template
  • JUnit tests for cache key template handling
  • Add copyright headers

missing com.netflix.util.Pair

I was trying to run the sampleapp but not able to compile my code due to the missing com.netflix.util.Pair file used in AbstractLoadBalancerAwareClient.java file.

Please suggest where can i get it?

Proposal for high level client APIs for best practices

Note: the latest design is here

Goals

In the past two years, Ribbon has been used as an inter-processing communication client with load balancing capabilities. Some best practices have been observed when using Ribbon together with other open source libraries. We feel it is best to offer such best practices in a form of a set of APIs in a new Ribbon module.

Here is that we want to achieve:

  • Use annotation/template to drive the process of a request creation as opposed to hand-craft the request
  • Use Hystrix for fault tolerance, fallback and call collapsing and make Ribbon client properties visible to Hystrix to close the gap of the two layers
  • Use cache, (e.g. EVCache) for low latency data access before reaching out to the actual resource
  • Built on top of reactive and non-blocking networking layer RxNetty
  • Protocol agnostic

The ultimate goal is that the clients created with this set of API should be more consistent, agile, reliable, efficient, and resilient.

Design proposal

The center pieces of the new API are AsyncRequest and RibbonRequest

public interface AsyncRequest<T> {
    public T execute();

    public Future<T> queue();

    public Observable<T> observe();

    public Observable<T> toObservable();
}

public interface RibbonRequest<T> extends AsyncRequest<T> {
    public RibbonRequest<HystrixResponse<T>> withHystrixInfo();
}

RequestTemplate is used to represent all the information, including cache providers, Hystrix fallback and any protocol specific information (e.g. URI for HTTP) required to create RibbonRequest. Information supplied to create the RequestTemplate may include variables which should be substituted with real values at the time of request creation.

/**
 * @param <I> request input entity type
 * @param <O> response entity type
 * @param <R> response meta data, e.g. HttpClientResponse
 */
public interface RequestTemplate<I, O, R> {

    RequestBuilder<O> requestBuilder();

    RequestTemplate<I, O, R> withFallbackProvider(FallbackProvider<O> fallbackProvider);

    RequestTemplate<I, O, R> withFallbackDeterminator(FallbackDeterminator<R> fallbackDeterminator);

    RequestTemplate<I, O, R> addCacheProvider(CacheProvider<O> cacheProvider, String cacheKeyTemplate);

    RequestTemplate<I, O, R> withHystrixCommandPropertiesDefaults(HystrixCommandProperties.Setter setter);

    RequestTemplate<I, O, R> withHystrixThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter setter);

    RequestTemplate<I, O, R> withHystrixCollapserPropertiesDefaults(HystrixCollapserProperties.Setter setter);

    public abstract class RequestBuilder<O> {
        public abstract RequestBuilder<O> withValue(String key, Object value);

        public abstract RibbonRequest<O> build();
    }
}

FallbackDeterminator is used to determine whether a response with protocol specific meta data should cause throw an error during Hystrix command execution and cause a fallback:

public interface FallbackDeterminator<T> {
    public boolean shouldTriggerFallback(T responseMetaData);
}

For example:

Ribbon.from(httpClient)
        .newRequestTemplate()
        .withFallbackDeterminator(new FallbackDeterminator<HttpClientResponse<ByteBuf>>() {
            @Override
            public boolean shouldTriggerFallback(
                    HttpClientResponse<ByteBuf> response) {
                return response.getStatus().code() >= 500;
            }
        })   

Once FallbackDeterminator determines that a protocol specific response should trigger Hystrix fallback, HystrixFallbackProvider is used to get the actual fallback:

public interface FallbackProvider<T> extends Func1<HystrixCommand<T>, Observable<T>> {
}

CacheProvider is the interface to provide access to cache of the resource

public interface CacheProvider<T> {
    Observable<T> get(String key);
}

Ribbon is the factory that creates

  • clients whose responsibility is to create protocol specific template
  • service object from the interface definition of a service contract defined by annotations
public final class Ribbon {

    private Ribbon() {
    }

    public static <I, O> RibbonHttpClient<I, O> from(HttpClient<I, O> transportClient) {
        // ...
    }

    public static <I, O, T> T create(Class<T> contract, HttpClient<I, O> transportClient) {
        // ...
    }
}

/**
 * @param <I> Request input entity type
 * @param <O> Response entity type
 * @param <T> Type of RequestTemplate
 */
public interface RibbonClient<I, O, T extends RequestTemplate<I, O, ?>> {
    public T newRequestTemplate();
}

The ribbon-transport module in Ribbon will provide clients used by Ribbon for different protocol with load balancing capabilities. You can also use any client created directly RxNetty if load balancing is not required.

The RibbonTransport in ribbon-transport module is the factory to create load balancing clients:

public final class RibbonTransport {

    private RibbonTransport() {
    }

    public static RxClient<ByteBuf, ByteBuf> newTcpClient(ILoadBalancer loadBalancer, IClientConfig config) {
        // ...
    }

    public static <I, O> RxClient<I, O> newTcpClient(ILoadBalancer loadBalancer, PipelineConfigurator<O, I> pipelineConfigurator, 
            IClientConfig config) {
        // ...
    }

    public static <I, O> RxClient<I, O> newTcpClient(PipelineConfigurator<O, I> pipelineConfigurator, 
            IClientConfig config) {
        // ...
    }

    // ...

    public static HttpClient<ByteBuf, ByteBuf> newHttpClient(IClientConfig config) {
        // ...
    }

    public static <I, O> HttpClient<I, O> newHttpClient(PipelineConfigurator<HttpClientResponse<O>, HttpClientRequest<I>> pipelineConfigurator, 
            ILoadBalancer loadBalancer, IClientConfig config) {
       // ...
    }

    public static <I, O> HttpClient<I, O> newHttpClient(PipelineConfigurator<HttpClientResponse<O>, HttpClientRequest<I>> pipelineConfigurator, 
            IClientConfig config) {
        // ...
    }
}

prefer service located in the same host

Hello,

We have the following setup in Amazon cloud: an ASG with multiple instances where each instance has the same collection of services. A layer of these services('application') accept traffic externally and delegate work to another layer of services('domain'). Each instance host has both layers. Every service is registered to Eureka instances that are located in some other ASG. 'application' services use Eureka to find a dependent 'domain' service to delegate its work to. Right now, we don't use Ribbon but we would like to integrate it too.
Question is:
Is there a way to code Ribbon so that it "prefers" service instances located in the same host as the calling service? If the service instance required, exists in the same host, and if the service instance is not marked as a no-go from Ribbon's circuit breaker, then it should be preferred so that we don't get the extra network hop. Does this makes sense? Or this will probably mess with the whole idea of load balancing?

ArtifactDescriptorException: Failed to read artifact descriptor for com.google.code.findbugs:annotations:jar:1.3+: ArtifactResolutionException: Failure to transfer com.google.code.findbugs:annotations:pom:1.3+

The following dependency from ribbon-core's pom.xml file seems to be causing the issue:

com.netflix.netflix-commons netflix-statistics 0.1.0 compile

this netflix-statistics pom.xml contains the following:

com.google.code.findbugs
annotations
1.3+
compile

it appears that maven doesn't like the '+'. i believe this is an ivy thing as i remember using it heavily back at netflix.

here's a link i found just now re: maven versions: http://docs.codehaus.org/display/MAVEN/Dependency+Mediation+and+Conflict+Resolution#DependencyMediationandConflictResolution-DependencyVersionRanges

Questions

Hello,

Some questions:

1). Is there a way to switch from Eureka-enabled service discovery to the static one (defined in a configuration file), during runtime? The opposite? I mean if the Eureka client somehow breaks, i would like to have a fallback case. I know that in the cloud, this does not seem to have much sense, but our first installation is in our data center.

2). What is the way to define or change the Role with which the load balancer chooses a server? Can multiple Roles be defined? e.g. A RoundRobin + Retry? Can this change be made and take effect during runtime?

3). We do all our network calls through Hystrix. Is it a good combination to use Hystrix over Ribbon over HttpClient? What about the failure resiliency features of Ribbon? Do they overlap with the ones in Hystrix? Can Ribbon use Hystrix information as a Role? What if Ribbon reports that a circuit breaker is open but Hystrix reports the opposite? Can we fully disable the falire resiliency features of Ribbon?

FollowRedirects impossible to turn off.

The following will always fail:

    @Test
    public void testRedirectNotFollowed() throws Exception {
        IClientConfig config = DefaultClientConfigImpl.getClientConfigWithDefaultValues();
        config.setProperty(CommonClientConfigKey.FollowRedirects, Boolean.FALSE);
        ClientFactory.registerClientFromProperties("myclient", config);
        RestClient client = (RestClient) ClientFactory.getNamedClient("myclient");
        HttpRequest request = HttpRequest.newBuilder().uri(new URI("http://jigsaw.w3.org/HTTP/300/302.html")).build();
        HttpResponse response = client.executeWithLoadBalancer(request);
        assertEquals(302, response.getStatus());      
    }

The culprit seems to be how jersey interacts with httpclient4 on a per-request basis.

https://java.net/jira/browse/JERSEY-1534 "ApacheHttpClient4 does not honor PROPERTY_FOLLOW_REDIRECTS, when set for a WebResource"

It seems that up through Jersey 2, the only way to get the redirects property respected is during the connection setup

The only way I was able to get redirects to turn off was to change NFHttpClient.init() and add:

params.setParameter(ClientPNames.HANDLE_REDIRECTS, Boolean.FALSE);

Redirects stopped getting followed.

There are a couple of options here:

  • Upgrade to Jersey 2.0 - probably non-trivial, not sure if/where it is on your road-map
  • Change NFHttpClientFactory.getNamedNFHttpClient to take a parameter for the followredirects flag, since it seems that an IClientConfig is available in the two places where this is called.

Provide plug-in mechanism for IClientConfig implementations

Ideally we would like to have a plug-in class which is invoked at application start up to set the default implementation class of IClientConfig. Then IClientConfig.Builder will always use that default implementation class, if no other implementation class is passed in as method arguments. This will ensure that APIs do not have to depend on an instance of IClientConfig passed. Instead, one will be created using the desired implementation.

ResponseTimeWeightedRule infinite loop

Hello,

I have run in to a problem i ResponseTimeWeightedRule, on the first request none of the servers has any request times stored in the load balancing information. This lead to maxTotalWeight being 0 and public Server choose(..) get stuck in an infinite loop when choosing a random index.

I am running Ribbon and Eureka just for testing on my local machine, here is the output for the load balancer information:

DynamicServerListLoadBalancer:{NFLoadBalancer:name=sessionmangement-client,current list of Servers=[Rubens-MacBook-Pro-5.local:9292, Rubens-MacBook-Pro-5.local:8282],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:Rubens-MacBook-Pro-5.local:9292; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 01:00:00 CET 1970; First connection made: Thu Jan 01 01:00:00 CET 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:Rubens-MacBook-Pro-5.local:8282; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 01:00:00 CET 1970; First connection made: Thu Jan 01 01:00:00 CET 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]

LoadBalancer Stack performance

Where can I find some documentation on the performance of the Ribbon stack?

I am in the process of extending it to wrap an Apache Http Client?

Server filtering logic should have better building blocks

Currently the server filtering logic is scattered in several classes, some of them are written as Rule, some of them are written as ServerListFilter. These logic includes:

Zone affinity
Zone avoidance
Availability filtering (circuit breaker logic)

It is hard to write more complicated filtering logic to arbitrarily combine more two or more filtering logics.

The proposal is to rewrite them as Predicate in Guava and support compositions of those predicates.

ArtifactDescriptorException: Failed to read artifact descriptor for com.netflix.servo:servo-core:jar:0.4.13

This happens when i add the following to my pom.xml:

    <dependency>
        <groupId>com.netflix.ribbon</groupId>
        <artifactId>ribbon-eureka</artifactId>
        <version>0.1.1</version>
    </dependency>

This is similar to the following Eureka issue that I created recently: Netflix/eureka#13.

I closed this issue because I found a workaround, but it's still lingering in eureka-based code, it appears.

Tracking this down, I see that eureka-core v1.1.37 has a dependency on an older, unreleased version of servo:

    <dependency>
        <groupId>com.netflix.servo</groupId>
        <artifactId>servo-core</artifactId>
        <version>0.4.13</version>
    </dependency>

here's the link to the eureka-core pom.xml: http://search.maven.org/remotecontent?filepath=com/netflix/eureka/eureka-core/1.1.37/eureka-core-1.1.37.pom

AbstractLoadBalancerAwareClient is not handling query param encoding correctly

AbstractLoadBalancerAwareClient.computeFinalUriWithLoadBalancer() rebuilds the URI but corrupts query parameters. Line 672 is:

newURI = new URI(scheme, theUrl.getUserInfo(), host, port, urlPath, theUrl.getQuery(), theUrl.getFragment());

It should be:

newURI = new URI(scheme, theUrl.getUserInfo(), host, port, urlPath, theUrl.getRawQuery(), theUrl.getFragment());

Here's a test that shows the problem:

import com.netflix.client.ClientException;
import com.netflix.niws.client.http.HttpClientRequest;
import com.netflix.niws.client.http.RestClient;
import org.junit.Assert;
import org.junit.Test;

import java.net.URI;
import java.net.URLEncoder;
import java.util.concurrent.atomic.AtomicReference;

public class RibbonTest {
    @Test
    public void showBadEncodingInRibbon() throws Exception {
        final String lotsOfToughChars = "éƎ&=*%!@#$%^&*()";

        final AtomicReference<URI> computedUri = new AtomicReference<>();
        RestClient restClient = new RestClient() {
            @Override
            protected HttpClientRequest computeFinalUriWithLoadBalancer(HttpClientRequest original) throws ClientException {
                HttpClientRequest httpClientRequest = super.computeFinalUriWithLoadBalancer(original);
                computedUri.set(httpClientRequest.getUri());
                return httpClientRequest;
            }
        };

        URI uri = new URI("http://foo.com:8080/bar?test=" + URLEncoder.encode(lotsOfToughChars, "UTF-8"));
        HttpClientRequest request = HttpClientRequest.newBuilder().setUri(uri).build();
        try {
            restClient.executeWithLoadBalancer(request);
        } catch (ClientException e) {
            // ignore
        }

        Assert.assertEquals(uri, computedUri.get());
    }
}

How to programmatically set the list of servers?

Hello,

I'm trying to programmatically define the list of servers. I followed the documentation stating that i need to get an instance of DefaultClientConfigImpl and then set all desired properties by calling setProperty(IClientConfigKey, Object). Neverthless, the CommonClientConfigKey enumeration (is this the one to use???) does not seem to have a relevant entry...
I guess this has to do with the dynamic changing of servers during runtime? One has to define the relevant archaius-specific configuration property in order for this to get enabled?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.