Hi Marius,
I'm building the service as follows: Service<ThriftClientRequest, byte[]>
service = ClientBuilder.safeBuild(clientBuilder);
And yes, I am use the serverset clusters with zookeeper. Below is my entire
client - thanks for the help. -Walter
package com.nextbigsound.finagle;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.thrift.protocol.TBinaryProtocol;
import scala.Option;
import scala.runtime.Nothing$;
import com.nextbigsound.thrift.generated.GeoCoder;
import com.nextbigsound.thrift.generated.GeoLocation;
import com.twitter.common.quantity.Amount;
import com.twitter.common.quantity.Time;
import com.twitter.common.zookeeper.ServerSet;
import com.twitter.common.zookeeper.ServerSetImpl;
import com.twitter.common.zookeeper.ZooKeeperClient;
import com.twitter.finagle.Service;
import com.twitter.finagle.builder.ClientBuilder;
import com.twitter.finagle.service.RetryPolicy;
import com.twitter.finagle.service.SimpleRetryPolicy;
import com.twitter.finagle.thrift.ClientId;
import com.twitter.finagle.thrift.ThriftClientFramedCodecFactory;
import com.twitter.finagle.thrift.ThriftClientRequest;
import com.twitter.finagle.zookeeper.ZookeeperServerSetCluster;
import com.twitter.util.Duration;
import com.twitter.util.Future;
import com.twitter.util.Try;
/**
* Wrapper around the GeoCoder service that exposes static methods.
*
* @author Walter
*
*/
public class GeoCoderClient implements GeoCoderConfig {
/*
* Code below here is specific to this service. Mirror the methods found in
* the ServiceIface interface, but make them static.
*/
public static Future<Void> ping() {
return client.ping();
}
public static Future<GeoLocation> forwardGeocode(String location) {
return client.forwardGeocode(location);
}
/*
* Code below here changes minimally between services. Please see CHANGEME
* tasks for more info.
*/
/**
* The Finagle client object - all calls will ultimately go through this.
*/
private static GeoCoder.ServiceIface client; // CHANGEME - replace
"GeoCoder" with the name of the thrift class
/**
* Perform all initialization required to start up this service. Block until
* service is ready.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static void init() {
Logger logger = Logger.getAnonymousLogger();
logger.setLevel(Level.OFF);
RetryPolicy<Try<Nothing$>> retryPolicy = newSimpleRetryPolicy<Try<Nothing$>>() {
@Override
public Duration backoffAt(int retry) {
if (retry > 3) {
return Duration.fromTimeUnit(16, TimeUnit.SECONDS);
} else {
return Duration.fromTimeUnit((int) java.lang.Math.pow(2.0, retry), TimeUnit.
SECONDS);
}
}
@Override
public boolean shouldRetry(Try<Nothing$> arg0) {
System.out.println("shouldRetry called");
return true;
}
};
ClientBuilder clientBuilder = ClientBuilder.get()
.codec(new ThriftClientFramedCodecFactory(Option.<ClientId> empty()))
.hostConnectionLimit(3)
.retryPolicy(retryPolicy)
.retries(Integer.MAX_VALUE)
.logger(logger);
// if we are using zookeeper to locate this service
if (ZOOKEEPER_ENABLED) {
// get connection to the zookeeper service
System.out.printf("Connecting to zookeeper at %s:%d%n", ZOOKEEPER_HOST,
ZOOKEEPER_PORT);
ZooKeeperClient zookeeperClient = new ZooKeeperClient(Amount.of(
ZOOKEEPER_CONNECT_TIMEOUT, Time.MILLISECONDS), new InetSocketAddress(
ZOOKEEPER_HOST, ZOOKEEPER_PORT));
ServerSet serverSet = new ServerSetImpl(zookeeperClient,
ZOOKEEPER_SERVICE_IDENTIFIER);
ZookeeperServerSetCluster cluster = newZookeeperServerSetCluster(serverSet);
System.out.printf("Connecting to service via Zookeeper.%n");
clientBuilder = clientBuilder.cluster(cluster);
}
// else use predefined address:port to connect to this service
else {
System.out.printf("Connecting to %s:%d%n", GEOCODER_HOST, GEOCODER_PORT);
clientBuilder = clientBuilder.hosts(new InetSocketAddress(GEOCODER_HOST,
GEOCODER_PORT));
}
// build the client
Service<ThriftClientRequest, byte[]> service =
ClientBuilder.safeBuild(clientBuilder);
// attach thrift to Finagle client
client = new GeoCoder.ServiceToClient(service, newTBinaryProtocol.Factory()); //
CHANGEME - replace "GeoCoder" with the name of the thrift class
}
/**
* Get it all started.
*/
static {
GeoCoderClient.init();
}
}
On Monday, July 30, 2012 12:43:45 PM UTC-4, you wrote:
> The retry policy is supposed to be applied here. What does the rest of
> your builder look like? Are you using .build() or .buildFactory()? Can you
> show some more code?
> Also, it looks like you’re using serverset clusters?
> marius.
> On Jul 27, 2012, at 3:18 PM, Walter Blaurock wrote:
> Hi Marius - thanks for the reply. I have tried this in the past, but when
> my server goes down, the client still throws an exception instead of
> retrying. It was because of this that I reached out to find other
> solutions, as I thought I was on the wrong path. The following is my
> current ClientBuilder code:
> ClientBuilder clientBuilder = ClientBuilder.get()
> .codec(new ThriftClientFramedCodecFactory(Option.<ClientId> empty()))
> .hostConnectionLimit(3)
> .retryPolicy(new SimpleRetryPolicy<Try<Nothing$>>() {
> @Override
> public Duration backoffAt(int retry) {
> if (retry > 3) {
> return Duration.fromTimeUnit(16, TimeUnit.SECONDS);
> } else {
> return Duration.fromTimeUnit((int) java.lang.Math.pow(2.0, retry),
> TimeUnit.SECONDS);
> }
> }
> @Override
> public boolean shouldRetry(Try<Nothing$> arg0) {
> System.out.println("Here");
> return true;
> }
> })
> .retries(3)
> .logger(logger);
> And when I make calls to my client, the call in progress when the server
> goes down throws something along the lines of:
> Exception in thread "main" com.twitter.finagle.ChannelClosedException:
> ChannelException at remote address: 10.1.1.165/10.1.1.165:34567
> at com.twitter.finagle.NoStacktrace(Unknown Source)
> Jul 27, 2012 5:58:08 PM
> com.twitter.common.zookeeper.ServerSetImpl$ServerSetWatcher
> notifyServerSetChange
> WARNING: server set empty!
> It was my hope that the retry policy would cause this to be attempted
> repeatedly until success, as opposed to throwing an exception. I guess I'm
> confused about when my retry policy comes into effect.
> Thanks,
> -Walter
> PS I tried Void instead of Nothing$, but had trouble getting that to
> compile: "The method retryPolicy(RetryPolicy<Try<Nothing$>>) in the type
> ClientBuilder<ThriftClientRequest,byte[],Nothing$,ClientConfig.Yes,ClientCo nfig.Yes>
> is not applicable for the arguments (RetryPolicy<Try<Void>>)"
> On Friday, July 27, 2012 4:20:24 PM UTC-4, you wrote:
>> On Jul 27, 2012, at 11:19 AM, Walter Blaurock wrote:
>> I have been using Finagle (with Java) for a few weeks, and have had a lot
>> of success with it. However, I'm having trouble wrapping my head around the
>> failover/retry behavior in my clients. It is my understanding that, by
>> default, Finagle will only retry a client request if nothing has been
>> written out on the wire to avoid issues with non-idempotent methods. I
>> certainly agree with this default behavior, but I'm looking for a way to
>> modify it (to retry all failures, for example). I was hoping that either
>> retryPolicy or retryFilter would help me out, but the first appears to deal
>> with backing off of subsequent retries, and I'm unable to find
>> documentation/samples for the second.
>> What is wish to accomplish is:
>> 1. start server / start client (in no particular order)
>> 2. once client and server are both up, client will start making
>> requests to server
>> 3. server goes down (for maintenance, unexpected network issues,
>> crash)
>> 4. client intentionally hangs/retries the current request until the
>> server is back up
>> 5. server comes back up
>> 6. client continues where it left off
>> What I'm currently seeing is:
>> 1. start server / start client (in no particular order)
>> 2. once client and server are both up, client will start making
>> requests to server
>> 3. server goes down (for maintenance, unexpected network issues,
>> crash)
>> 4. client throws exception / onFailure callback is called for each
>> request issued while server is down
>> 5. server comes back up
>> 6. client soon reconnects to server, and requests proceed as expected
>> You can simply specify your own retry policy. To emulate your particular
>> example, you would simply do this (in Java-ish, since I see you are using
>> Java):
>> RetryPolicy<Try<Void>> r = new SimpleRetryPolicy<Try<Void>>() {
>> public boolean shouldRetry(Try<Void> a) {
>> return true;
>> }
>> public Duration backoffAt(int retry) {
>> return Duration.fromTimeUnit(0, TimeUnit.MILLISECONDS);
>> }
>> };
>> But I highly recommend adding some sort of nonzero, growing back off, for
>> an example of that see:
>> https://github.com/twitter/finagle/blob/master/finagle-core/src/test/...
>> marius.