High-availability pooled Thrift Client implementation (source code sharing), pooled thrift

Source: Internet
Author: User
Tags failover

High-availability pooled Thrift Client implementation (source code sharing), pooled thrift

This article will share a highly available pooled Thrift Client and its source code implementation. You are welcome to read and use the source code (Github). At the same time, you are welcome to provide valuable comments and suggestions. I will continue to improve it.

The main target readers of this article are children's shoes who have certain knowledge about Thrift and use it. For example, they do not know much about Thrift basic knowledge or want to repeat the basic knowledge, we recommend that you read this article "a beautiful encounter with Thrift" first.

Next, go to the topic.

Why do we need such a component?

We know that Thrift is an RPC framework system that can easily develop and call cross-language RPC services. However, it does not provide Smart Client [1] for multiple servers ]. For example, you have a service deployed on 116.31.1.1 and 116.31.1.2 respectively. When you need to call a remote method of the service from the Client, you can only explicitly specify one of 116.31.1.1 or 116.31.1.2 in the code. In this case, when you call the service, you cannot predict whether the service corresponding to the specified IP address is available. When the service is unavailable, it cannot be automatically implicitly switched to the service corresponding to the call of another IP address. That is to say, the service status is not transparent to you and cannot achieve load balancing and high availability of the service.

In addition, when you call a remote method, you have to create a connection each time. When the Request volume is large, the service resources that are constantly created and deleted are huge.

Therefore, we need such a component to make the service status transparent and achieve load balancing and high availability at the underlying layer, so that you can focus on the implementation of business logic and improve work efficiency and service quality. Next we will analyze this component in detail.

What can it do? Features
  • Chained API call, concise and intuitive
  • Complete default configuration, no need to worry about errors caused by incomplete Configuration During the call
  • Pooled connection objects to efficiently manage the connection Lifecycle
  • Automatic isolation and recovery of abnormal services
  • Multiple configurable Load Balancing policies that support random, round robin, weight, and hash
  • Multiple configurable service levels and automatic service downgrade Based on the Service Level
How to use it?

The latest version is 1.0.1 (Click here to focus on updates to the latest version), first introduce the thriftj-1.0.1.jar in the project, or add in the Maven dependency:

<dependency>    <groupId>com.github.cyfonly</groupId>    <artifactId>thriftj</artifactId>    <version>1.0.1</version></dependency>

Note that ThriftJ is built based on slf4j, so you need to add specific log implementation dependencies in the project, such as log4j or logback.

Then, in the project, refer to the following code for calling:

// Thrift server LIST private static final String servers = "127.0.0.1: 10001,127.0 .0.1: 10002"; // TTransport validators ConnectionValidator validator = new ConnectionValidator () {@ Override public boolean isValid (TTransport object) {return object. isOpen () ;}}; // connection object pool configuration GenericKeyedObjectPoolConfig poolConfig = new timeout (); // failover policy FailoverStrategy failoverStrategy = new FailoverStrategy (); // construct the thrifle tclient object and configure final thrifle tclient = new thrifle tclient (); thrifle tclient. servers (servers ). loadBalance (Constant. loadBalance. RANDOM ). connectionValidator (validator ). poolConfig (poolConfig ). failoverStrategy (failoverStrategy ). connTimeout (5 ). backupServers (""). serviceLevel (Constant. serviceLevel. NOT_EMPTY ). start (); // print the List of available services obtained from ThriftServer> servers = thritclient. getAvailableServers (); for (ThriftServer server: servers) {System. out. println (server. getHost () + ":" + server. getPort ();} // service call if (servers. size ()> 0) {try {TestThriftJ. client client = thrifle tclient. iface (TestThriftJ. client. class); QryResult result = client. qryTest (1); System. out. println ("result [code =" + result. code + "msg =" + result. msg + "]");} catch (Throwable t) {logger. error ("------------- exception happen", t );}}

Tip: Except that servers must be configured, other configurations are optional (use the default configuration)

How is it designed and implemented? Overall Design

Management of connection pool object factories and connection objects

Based on the KeyedPooledObjectFactory in the commons-pool2, ThriftServer is the key and TTransport is the value. The key code is as follows:

@Overridepublic PooledObject<TTransport> makeObject(ThriftServer thriftServer) throws Exception {    TSocket tsocket = new TSocket(thriftServer.getHost(), thriftServer.getPort());    tsocket.setTimeout(timeout);    TFramedTransport transport = new TFramedTransport(tsocket);        transport.open();    DefaultPooledObject<TTransport> result = new DefaultPooledObject<TTransport>(transport);    logger.trace("Make new thrift connection: {}:{}", thriftServer.getHost(), thriftServer.getPort());        return result;}@Overridepublic boolean validateObject(ThriftServer thriftServer, PooledObject<TTransport> pooledObject) {    boolean isValidate;    try {        if (failoverChecker == null) {            isValidate = pooledObject.getObject().isOpen();        } else {            ConnectionValidator validator = failoverChecker.getConnectionValidator();            isValidate = pooledObject.getObject().isOpen() && (validator == null || validator.isValid(pooledObject.getObject()));        }    } catch (Throwable e) {        logger.warn("Fail to validate tsocket: {}:{}", new Object[]{thriftServer.getHost(), thriftServer.getPort(), e});        isValidate = false;    }    if (failoverChecker != null && !isValidate) {        failoverChecker.getFailoverStrategy().fail(thriftServer);    }    logger.info("ValidateObject isValidate:{}", isValidate);        return isValidate;}@Overridepublic void destroyObject(ThriftServer thriftServer, PooledObject<TTransport> pooledObject) throws Exception {    TTransport transport = pooledObject.getObject();    if (transport != null) {        transport.close();        logger.trace("Close thrift connection: {}:{}", thriftServer.getHost(), thriftServer.getPort());    }}

When using a connection object, create a connection pool based on the user-defined connection pool configuration, and obtain, return, and clear the connection object, and close the connection pool. The key code is as follows:

public DefaultThriftConnectionPool(KeyedPooledObjectFactory<ThriftServer, TTransport> factory, GenericKeyedObjectPoolConfig config) {connections = new GenericKeyedObjectPool<>(factory, config);}@Overridepublic TTransport getConnection(ThriftServer thriftServer) {try {return connections.borrowObject(thriftServer);} catch (Exception e) {logger.warn("Fail to get connection for {}:{}", new Object[]{thriftServer.getHost(), thriftServer.getPort(), e});throw new RuntimeException(e);}}@Overridepublic void returnConnection(ThriftServer thriftServer, TTransport transport) {connections.returnObject(thriftServer, transport);}@Overridepublic void returnBrokenConnection(ThriftServer thriftServer, TTransport transport) {try {connections.invalidateObject(thriftServer, transport);} catch (Exception e) {logger.warn("Fail to invalid object:{},{}", new Object[] { thriftServer, transport, e });}}@Overridepublic void close() {connections.close();}@Overridepublic void clear(ThriftServer thriftServer) {connections.clear(thriftServer);}
Automatic isolation and recovery of abnormal services

To make the service status transparent, you must monitor, isolate, and restore the service at the underlying layer. In ThriftJ, when you call thritclient, a thread is started to perform asynchronous monitoring on the service. You can specify the validation rules (corresponding to ConnectionValidator) and failover policies (corresponding to FailoverStrategy, you can specify the number of failures, failure duration, and recovery duration ). By default, the service verification rule is to determine whether TTransport is enabled, that is:

if (this.validator == null) {  this.validator = new ConnectionValidator() {    @Override    public boolean isValid(TTransport object) {      return object.isOpen();    }  };}

The default failover policy is

  • Number of failures: 10 (times), indicating that the service is considered to be invalid 10 times after the ConnectionValidator test fails. The failure duration must be used together.
  • Duration: 1 (minute) indicates that the service is considered to be invalid only when the time of the first test fails continuously reaches this value within a test period. This can be used together with the number of failures.
  • Recovery Duration: 1 minute, indicating that the service is restored after the service is declared invalid and isolated.

The above functions are implemented based on Guava cache. The key code is as follows:

/*** Use the default failover policy */public FailoverStrategy () {this (DEFAULT_FAIL_COUNT, DEFAULT_FAIL_DURATION, DEFAULT_RECOVER_DURATION );} /*** custom failover policy * @ param failCount failure count * @ param failDuration failure duration * @ param recoverDuration restoration duration */public FailoverStrategy (final int failCount, long failDuration, long recoverDuration) {this. failDuration = failDuration; this. failedList = CacheBuilder. newBuilder (). weakKeys (). expireAfterWrite (recoverDuration, TimeUnit. MILLISECONDS ). build (); this. failCountMap = CacheBuilder. newBuilder (). weakKeys (). build (new CacheLoader <T, EvictingQueue <Long> () {@ Overridepublic EvictingQueue <Long> load (T key) throws Exception {return EvictingQueue. create (failCount) ;}});} public void fail (T object) {logger.info ("Server {}:{} failed. ", (ThriftServer) object ). getHost (), (ThriftServer) object ). getPort (); boolean addToFail = false; try {EvictingQueue <Long> evictingQueue = failCountMap. get (object); synchronized (evictingQueue) {evictingQueue. add (System. currentTimeMillis (); if (evictingQueue. remainingCapacity () = 0 & evictingQueue. element ()> = (System. currentTimeMillis ()-failDuration) {addToFail = true ;}} catch (ExecutionException e) {logger. error ("Ops. ", e);} if (addToFail) {failedList. put (object, Boolean. TRUE); logger.info ("Server {}:{} failed. add to fail list. ", (ThriftServer) object ). getHost (), (ThriftServer) object ). getPort () ;}} public Set <T> getFailed () {return failedList. asMap (). keySet ();}
Server Load balancer

ThriftJ provides four optional Load Balancing policies:

  • Random
  • Round Robin
  • Weight
  • Hash

The random algorithm is used by default when the user does not explicitly specify it. The implementation of specific algorithms is not described too much here.

Note that ThriftJ strictly regulates the call semantics. For example, when using a hash policy, you must specify the hash key. When using other non-hash policies, you must not specify the key, avoid ambiguity.

Service level and service downgrade

ThriftJ provides a variety of configurable service levels and downgrade services based on the service level. The relationship is as follows:

  • SERVERS_ONLY: The highest level. Only available services in the configured servers list are returned.
  • ALL_SERVERS: medium level. When all services in the servers list are unavailable, the available services in the backupServers list are returned.
  • NOT_EMPTY: minimum level. When all services in the servers and backupServers lists are unavailable, all services in the servers list are returned.

The default service level of ThriftJ is NOT_EMPTY. The key code for service downgrade is as follows:

private List<ThriftServer> getAvailableServers(boolean all) {List<ThriftServer> returnList = new ArrayList<>();Set<ThriftServer> failedServers = failoverStrategy.getFailed();for (ThriftServer thriftServer : serverList) {if (!failedServers.contains(thriftServer))returnList.add(thriftServer);}if (this.serviceLevel == Constant.ServiceLevel.SERVERS_ONLY) {return returnList;}if ((all || returnList.isEmpty()) && !backupServerList.isEmpty()) {for (ThriftServer thriftServer : backupServerList) {if (!failedServers.contains(thriftServer))returnList.add(thriftServer);}}if (this.serviceLevel == Constant.ServiceLevel.ALL_SERVERS) {return returnList;}if(returnList.isEmpty()){returnList.addAll(serverList);}return returnList;}
I have something to say

The promotion of technology comes from selfless sharing. Sharing Good technologies or tools won't make you lose anything. Instead, you can make it better after you study and communicate with each other. Don't worry that the tools you write are not good enough. Don't be afraid that your technology is not good enough. Who can go to heaven in one step?

Please love your love!

 

 

[1]Smart ClientFor example, zooclient can automatically discover cluster service nodes, automatic failover, and Server Load balancer.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.