基于zookeeper、连接池、Failover/LoadBalance等改造Thrift 服务化

来源:互联网 发布:火箭nba 知乎 编辑:程序博客网 时间:2024/06/05 04:05

对于Thrift服务化的改造,主要是客户端,可以从如下几个方面进行:

1.服务端的服务注册,客户端自动发现,无需手工修改配置,这里我们使用zookeeper,但由于zookeeper本身提供的客户端使用较为复杂,因此采用curator-recipes工具类进行处理服务的注册与发现。

2.客户端使用连接池对服务调用进行管理,提升性能,这里我们使用Apache Commons项目commons-pool,可以大大减少代码的复杂度。

3.关于Failover/LoadBalance,由于zookeeper的watcher,当服务端不可用是及时通知客户端,并移除不可用的服务节点,而LoadBalance有很多算法,这里我们采用随机加权方式,也是常有的负载算法,至于其他的算法介绍参考:常见的负载均衡的基本算法。

4.使thrift服务的注册和发现可以基于spring配置,可以提供很多的便利。

5.其他的改造如:

1)通过动态代理实现client和server端的交互细节透明化,让用户只需通过服务方提供的接口进行访问

2)Thrift通过两种方式调用服务Client和Iface

// *) Client API 调用(EchoService.Client)client.echo("hello lilei");  ---(1)// *) Service 接口 调用(EchoService.Iface)service.echo("hello lilei");  ---(2)
Client API的方式, 不推荐, 我们推荐Service接口的方式(服务化)。

下面我们来一一实现:

一、pom.xml引入依赖jar包

<dependency><groupId>org.apache.thrift</groupId><artifactId>libthrift</artifactId><version>0.9.2</version></dependency><dependency><groupId>commons-pool</groupId><artifactId>commons-pool</artifactId><version>1.6</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.0.9.RELEASE</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.6</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.7.1</version></dependency>

二、使用zookeeper管理服务节点配置

RPC服务往平台化的方向发展, 会屏蔽掉更多的服务细节(服务的IP地址集群, 集群的扩容和迁移), 只暴露服务接口. 这部分的演化, 使得server端和client端完全的解耦合. 两者的交互通过ConfigServer(MetaServer)的中介角色来搭线。

注: 该图源自dubbo的官网
这边借助Zookeeper来扮演该角色, server扮演发布者的角色, 而client扮演订阅者的角色.

Zookeeper是分布式应用协作服务. 它实现了paxos的一致性算法, 在命名管理/配置推送/数据同步/主从切换方面扮演重要的角色。 其数据组织类似文件系统的目录结构: 


每个节点被称为znode, 为znode节点依据其特性, 又可以分为如下类型:
  1). PERSISTENT: 永久节点
  2). EPHEMERAL: 临时节点, 会随session(client disconnect)的消失而消失
  3). PERSISTENT_SEQUENTIAL: 永久节点, 其节点的名字编号是单调递增的
  4). EPHEMERAL_SEQUENTIAL: 临时节点, 其节点的名字编号是单调递增的
  注: 临时节点不能成为父节点
  Watcher观察模式, client可以注册对节点的状态/内容变更的事件回调机制. 其Event事件的两类属性需要关注下:
  1). KeeperState: Disconnected,SyncConnected,Expired
  2). EventType: None,NodeCreated,NodeDeleted,NodeDataChanged,NodeChildrenChanged
RPC服务端:
  作为具体业务服务的RPC服务发布方, 对其自身的服务描述由以下元素构成.
  1). namespace: 命名空间,来区分不同应用 
  2). service: 服务接口, 采用发布方的类全名来表示
  3). version: 版本号
  借鉴了Maven的GAV坐标系, 三维坐标系更符合服务平台化的大环境.
  *) 数据模型的设计
  具体RPC服务的注册路径为: /rpc/{namespace}/{service}/{version}, 该路径上的节点都是永久节点
  RPC服务集群节点的注册路径为: /rpc/{namespace}/{service}/{version}/{ip:port:weight}, 末尾的节点是临时节点.

1.定义Zookeeper的客户端的管理

ZookeeperFactory.java

package cn.slimsmart.thrift.rpc.zookeeper;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.retry.ExponentialBackoffRetry;import org.springframework.beans.factory.FactoryBean;import org.springframework.util.StringUtils;/** * 获取zookeeper客户端链接 */public class ZookeeperFactory implements FactoryBean<CuratorFramework> {private String zkHosts;// session超时private int sessionTimeout = 30000;private int connectionTimeout = 30000;// 共享一个zk链接private boolean singleton = true;// 全局path前缀,常用来区分不同的应用private String namespace;private final static String ROOT = "rpc";private CuratorFramework zkClient;public void setZkHosts(String zkHosts) {this.zkHosts = zkHosts;}public void setSessionTimeout(int sessionTimeout) {this.sessionTimeout = sessionTimeout;}public void setConnectionTimeout(int connectionTimeout) {this.connectionTimeout = connectionTimeout;}public void setSingleton(boolean singleton) {this.singleton = singleton;}public void setNamespace(String namespace) {this.namespace = namespace;}public void setZkClient(CuratorFramework zkClient) {this.zkClient = zkClient;}@Overridepublic CuratorFramework getObject() throws Exception {if (singleton) {if (zkClient == null) {zkClient = create();zkClient.start();}return zkClient;}return create();}@Overridepublic Class<?> getObjectType() {return CuratorFramework.class;}@Overridepublic boolean isSingleton() {return singleton;}public CuratorFramework create() throws Exception {if (StringUtils.isEmpty(namespace)) {namespace = ROOT;} else {namespace = ROOT +"/"+ namespace;}return create(zkHosts, sessionTimeout, connectionTimeout, namespace);}public static CuratorFramework create(String connectString, int sessionTimeout, int connectionTimeout, String namespace) {CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();return builder.connectString(connectString).sessionTimeoutMs(sessionTimeout).connectionTimeoutMs(30000).canBeReadOnly(true).namespace(namespace).retryPolicy(new ExponentialBackoffRetry(1000, Integer.MAX_VALUE)).defaultData(null).build();}public void close() {if (zkClient != null) {zkClient.close();}}}

2.服务端注册服务

由于服务端配置需要获取本机的IP地址,因此定义IP获取接口

ThriftServerIpResolve.java

package cn.slimsmart.thrift.rpc.zookeeper;/** *  * 解析thrift-server端IP地址,用于注册服务 * 1) 可以从一个物理机器或者虚机的特殊文件中解析 * 2) 可以获取指定网卡序号的Ip * 3) 其他 */public interface ThriftServerIpResolve {String getServerIp() throws Exception;void reset();//当IP变更时,将会调用reset方法static interface IpRestCalllBack{public void rest(String newIp);}}
可以对该接口做不通的实现,下面我们基于网卡获取IP地址,也可以通过配置serverIp
ThriftServerIpLocalNetworkResolve.java

package cn.slimsmart.thrift.rpc.zookeeper;import java.net.Inet6Address;import java.net.InetAddress;import java.net.NetworkInterface;import java.net.SocketException;import java.util.Enumeration;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 解析网卡Ip * */public class ThriftServerIpLocalNetworkResolve implements ThriftServerIpResolve {private Logger logger = LoggerFactory.getLogger(getClass());//缓存private String serverIp;public void setServerIp(String serverIp) {this.serverIp = serverIp;}@Overridepublic String getServerIp() {if (serverIp != null) {return serverIp;}// 一个主机有多个网络接口try {Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();while (netInterfaces.hasMoreElements()) {NetworkInterface netInterface = netInterfaces.nextElement();// 每个网络接口,都会有多个"网络地址",比如一定会有lookback地址,会有siteLocal地址等.以及IPV4或者IPV6 .Enumeration<InetAddress> addresses = netInterface.getInetAddresses();while (addresses.hasMoreElements()) {InetAddress address = addresses.nextElement();if(address instanceof Inet6Address){continue;}if (address.isSiteLocalAddress() && !address.isLoopbackAddress()) {serverIp = address.getHostAddress();logger.info("resolve server ip :"+ serverIp);continue;}}}} catch (SocketException e) {e.printStackTrace();}return serverIp;}@Overridepublic void reset() {serverIp = null;}}
接下来我们定义发布服务接口,并实现将服务信息(服务接口、版本号,IP、port、weight)发布到zookeeper中。
ThriftServerAddressRegister.java

package cn.slimsmart.thrift.rpc.zookeeper;/** * 发布服务地址及端口到服务注册中心,这里是zookeeper服务器 */public interface ThriftServerAddressRegister {/** * 发布服务接口 * @param service 服务接口名称,一个产品中不能重复 * @param version 服务接口的版本号,默认1.0.0 * @param address 服务发布的地址和端口 */void register(String service,String version,String address);}
实现:ThriftServerAddressRegisterZookeeper.java

package cn.slimsmart.thrift.rpc.zookeeper;import java.io.UnsupportedEncodingException;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.imps.CuratorFrameworkState;import org.apache.zookeeper.CreateMode;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.util.StringUtils;import cn.slimsmart.thrift.rpc.ThriftException;/** *  注册服务列表到Zookeeper */public class ThriftServerAddressRegisterZookeeper implements ThriftServerAddressRegister{private Logger logger = LoggerFactory.getLogger(getClass());private CuratorFramework zkClient;public ThriftServerAddressRegisterZookeeper(){}public ThriftServerAddressRegisterZookeeper(CuratorFramework zkClient){this.zkClient = zkClient;}public void setZkClient(CuratorFramework zkClient) {this.zkClient = zkClient;}@Overridepublic void register(String service, String version, String address) {if(zkClient.getState() == CuratorFrameworkState.LATENT){zkClient.start();}if(StringUtils.isEmpty(version)){version="1.0.0";}//临时节点try {zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/"+service+"/"+version+"/"+address);} catch (UnsupportedEncodingException e) {logger.error("register service address to zookeeper exception:{}",e);throw new ThriftException("register service address to zookeeper exception: address UnsupportedEncodingException", e);} catch (Exception e) {logger.error("register service address to zookeeper exception:{}",e);throw new ThriftException("register service address to zookeeper exception:{}", e);}}public void close(){zkClient.close();}}

3.客户端发现服务

定义获取服务地址接口

ThriftServerAddressProvider.java

package cn.slimsmart.thrift.rpc.zookeeper;import java.net.InetSocketAddress;import java.util.List;/** * thrift server-service地址提供者,以便构建客户端连接池 */public interface ThriftServerAddressProvider {//获取服务名称String getService();/** * 获取所有服务端地址 * @return */    List<InetSocketAddress> findServerAddressList();    /**     * 选取一个合适的address,可以随机获取等'     * 内部可以使用合适的算法.     * @return     */    InetSocketAddress selector();    void close();}
基于zookeeper服务地址自动发现实现:ThriftServerAddressProviderZookeeper.java
package cn.slimsmart.thrift.rpc.zookeeper;import java.net.InetSocketAddress;import java.util.ArrayList;import java.util.Collections;import java.util.HashSet;import java.util.LinkedList;import java.util.List;import java.util.Queue;import java.util.Set;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.imps.CuratorFrameworkState;import org.apache.curator.framework.recipes.cache.ChildData;import org.apache.curator.framework.recipes.cache.PathChildrenCache;import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;/** * 使用zookeeper作为"config"中心,使用apache-curator方法库来简化zookeeper开发 */public class ThriftServerAddressProviderZookeeper implements ThriftServerAddressProvider, InitializingBean {private Logger logger = LoggerFactory.getLogger(getClass());// 注册服务private String service;// 服务版本号private String version = "1.0.0";private PathChildrenCache cachedPath;private CuratorFramework zkClient;// 用来保存当前provider所接触过的地址记录// 当zookeeper集群故障时,可以使用trace中地址,作为"备份"private Set<String> trace = new HashSet<String>();private final List<InetSocketAddress> container = new ArrayList<InetSocketAddress>();private Queue<InetSocketAddress> inner = new LinkedList<InetSocketAddress>();private Object lock = new Object();// 默认权重private static final Integer DEFAULT_WEIGHT = 1;public void setService(String service) {this.service = service;}public void setVersion(String version) {this.version = version;}public ThriftServerAddressProviderZookeeper() {}public ThriftServerAddressProviderZookeeper(CuratorFramework zkClient) {this.zkClient = zkClient;}public void setZkClient(CuratorFramework zkClient) {this.zkClient = zkClient;}@Overridepublic void afterPropertiesSet() throws Exception {// 如果zk尚未启动,则启动if (zkClient.getState() == CuratorFrameworkState.LATENT) {zkClient.start();}buildPathChildrenCache(zkClient, getServicePath(), true);cachedPath.start(StartMode.POST_INITIALIZED_EVENT);}private String getServicePath(){return "/" + service + "/" + version;}private void buildPathChildrenCache(final CuratorFramework client, String path, Boolean cacheData) throws Exception {cachedPath = new PathChildrenCache(client, path, cacheData);cachedPath.getListenable().addListener(new PathChildrenCacheListener() {@Overridepublic void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {PathChildrenCacheEvent.Type eventType = event.getType();switch (eventType) {case CONNECTION_RECONNECTED:logger.info("Connection is reconection.");break;case CONNECTION_SUSPENDED:logger.info("Connection is suspended.");break;case CONNECTION_LOST:logger.warn("Connection error,waiting...");return;default://}// 任何节点的时机数据变动,都会rebuild,此处为一个"简单的"做法.cachedPath.rebuild();rebuild();}protected void rebuild() throws Exception {List<ChildData> children = cachedPath.getCurrentData();if (children == null || children.isEmpty()) {// 有可能所有的thrift server都与zookeeper断开了链接// 但是,有可能,thrift client与thrift server之间的网络是良好的// 因此此处是否需要清空container,是需要多方面考虑的.container.clear();logger.error("thrift server-cluster error....");return;}List<InetSocketAddress> current = new ArrayList<InetSocketAddress>();String path = null;for (ChildData data : children) {path = data.getPath();logger.debug("get path:"+path);path = path.substring(getServicePath().length()+1);logger.debug("get serviceAddress:"+path);String address = new String(path.getBytes(), "utf-8");current.addAll(transfer(address));trace.add(address);}Collections.shuffle(current);synchronized (lock) {container.clear();container.addAll(current);inner.clear();inner.addAll(current);}}});}private List<InetSocketAddress> transfer(String address) {String[] hostname = address.split(":");Integer weight = DEFAULT_WEIGHT;if (hostname.length == 3) {weight = Integer.valueOf(hostname[2]);}String ip = hostname[0];Integer port = Integer.valueOf(hostname[1]);List<InetSocketAddress> result = new ArrayList<InetSocketAddress>();// 根据优先级,将ip:port添加多次到地址集中,然后随机取地址实现负载for (int i = 0; i < weight; i++) {result.add(new InetSocketAddress(ip, port));}return result;}@Overridepublic List<InetSocketAddress> findServerAddressList() {return Collections.unmodifiableList(container);}@Overridepublic synchronized InetSocketAddress selector() {if (inner.isEmpty()) {if (!container.isEmpty()) {inner.addAll(container);} else if (!trace.isEmpty()) {synchronized (lock) {for (String hostname : trace) {container.addAll(transfer(hostname));}Collections.shuffle(container);inner.addAll(container);}}}return inner.poll();}@Overridepublic void close() {try {            cachedPath.close();            zkClient.close();        } catch (Exception e) {        }}@Overridepublic String getService() {return service;}}
对此接口还做了一种实现,通过配置获取服务地址,参考附件:FixedAddressProvider.java

三、服务端服务注册实现

ThriftServiceServerFactory.java

package cn.slimsmart.thrift.rpc;import java.lang.instrument.IllegalClassFormatException;import java.lang.reflect.Constructor;import org.apache.thrift.TProcessor;import org.apache.thrift.TProcessorFactory;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.server.TServer;import org.apache.thrift.server.TThreadedSelectorServer;import org.apache.thrift.transport.TFramedTransport;import org.apache.thrift.transport.TNonblockingServerSocket;import org.springframework.beans.factory.InitializingBean;import org.springframework.util.StringUtils;import cn.slimsmart.thrift.rpc.zookeeper.ThriftServerAddressRegister;import cn.slimsmart.thrift.rpc.zookeeper.ThriftServerIpLocalNetworkResolve;import cn.slimsmart.thrift.rpc.zookeeper.ThriftServerIpResolve;/** * 服务端注册服务工厂 */public class ThriftServiceServerFactory implements InitializingBean {// 服务注册本机端口private Integer port = 8299;// 优先级private Integer weight = 1;// default// 服务实现类private Object service;// serice实现类//服务版本号private String version;// 解析本机IPprivate ThriftServerIpResolve thriftServerIpResolve;//服务注册private ThriftServerAddressRegister thriftServerAddressRegister;private ServerThread serverThread;public void setPort(Integer port) {this.port = port;}public void setWeight(Integer weight) {this.weight = weight;}public void setService(Object service) {this.service = service;}public void setVersion(String version) {this.version = version;}public void setThriftServerIpResolve(ThriftServerIpResolve thriftServerIpResolve) {this.thriftServerIpResolve = thriftServerIpResolve;}public void setThriftServerAddressRegister(ThriftServerAddressRegister thriftServerAddressRegister) {this.thriftServerAddressRegister = thriftServerAddressRegister;}@Overridepublic void afterPropertiesSet() throws Exception {if (thriftServerIpResolve == null) {thriftServerIpResolve = new ThriftServerIpLocalNetworkResolve();}String serverIP = thriftServerIpResolve.getServerIp();if (StringUtils.isEmpty(serverIP)) {throw new ThriftException("cant find server ip...");}String hostname = serverIP + ":" + port + ":" + weight;Class<?> serviceClass = service.getClass();// 获取实现类接口Class<?>[] interfaces = serviceClass.getInterfaces();if (interfaces.length == 0) {throw new IllegalClassFormatException("service-class should implements Iface");}// reflect,load "Processor";TProcessor processor = null;String serviceName = null;for (Class<?> clazz : interfaces) {String cname = clazz.getSimpleName();if (!cname.equals("Iface")) {continue;}serviceName = clazz.getEnclosingClass().getName();String pname = serviceName + "$Processor";try {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Class<?> pclass = classLoader.loadClass(pname);if (!TProcessor.class.isAssignableFrom(pclass)) {continue;}Constructor<?> constructor = pclass.getConstructor(clazz);processor = (TProcessor) constructor.newInstance(service);break;} catch (Exception e) {//}}if (processor == null) {throw new IllegalClassFormatException("service-class should implements Iface");}//需要单独的线程,因为serve方法是阻塞的.serverThread = new ServerThread(processor, port);serverThread.start();// 注册服务if (thriftServerAddressRegister != null) {thriftServerAddressRegister.register(serviceName, version, hostname);}}class ServerThread extends Thread {private TServer server;ServerThread(TProcessor processor, int port) throws Exception {TNonblockingServerSocket serverTransport = new TNonblockingServerSocket(port);TThreadedSelectorServer.Args tArgs = new TThreadedSelectorServer.Args(serverTransport);  TProcessorFactory processorFactory = new TProcessorFactory(processor);tArgs.processorFactory(processorFactory);tArgs.transportFactory(new TFramedTransport.Factory());  tArgs.protocolFactory( new TBinaryProtocol.Factory(true, true)); server = new TThreadedSelectorServer(tArgs);}@Overridepublic void run(){try{//启动服务server.serve();}catch(Exception e){//}}public void stopServer(){server.stop();}}public void close() {serverThread.stopServer();}}

四、客户端获取服务代理及连接池实现
客户端连接池实现:ThriftClientPoolFactory.java

package cn.slimsmart.thrift.rpc;import java.net.InetSocketAddress;import org.apache.commons.pool.BasePoolableObjectFactory;import org.apache.thrift.TServiceClient;import org.apache.thrift.TServiceClientFactory;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.TFramedTransport;import org.apache.thrift.transport.TSocket;import org.apache.thrift.transport.TTransport;import cn.slimsmart.thrift.rpc.zookeeper.ThriftServerAddressProvider;/** * 连接池,thrift-client for spring */public class ThriftClientPoolFactory extends BasePoolableObjectFactory<TServiceClient> {private final ThriftServerAddressProvider serverAddressProvider;private final TServiceClientFactory<TServiceClient> clientFactory;private PoolOperationCallBack callback;protected ThriftClientPoolFactory(ThriftServerAddressProvider addressProvider, TServiceClientFactory<TServiceClient> clientFactory) throws Exception {this.serverAddressProvider = addressProvider;this.clientFactory = clientFactory;}protected ThriftClientPoolFactory(ThriftServerAddressProvider addressProvider, TServiceClientFactory<TServiceClient> clientFactory,PoolOperationCallBack callback) throws Exception {this.serverAddressProvider = addressProvider;this.clientFactory = clientFactory;this.callback = callback;}static interface PoolOperationCallBack {// 销毁client之前执行void destroy(TServiceClient client);// 创建成功是执行void make(TServiceClient client);}public void destroyObject(TServiceClient client) throws Exception {if (callback != null) {try {callback.destroy(client);} catch (Exception e) {//}}TTransport pin = client.getInputProtocol().getTransport();pin.close();}public boolean validateObject(TServiceClient client) {TTransport pin = client.getInputProtocol().getTransport();return pin.isOpen();}@Overridepublic TServiceClient makeObject() throws Exception {InetSocketAddress address = serverAddressProvider.selector();TSocket tsocket = new TSocket(address.getHostName(), address.getPort());TTransport transport = new TFramedTransport(tsocket);TProtocol protocol = new TBinaryProtocol(transport);TServiceClient client = this.clientFactory.getClient(protocol);transport.open();if (callback != null) {try {callback.make(client);} catch (Exception e) {//}}return client;}}
客户端服务代理工厂实现:ThriftServiceClientProxyFactory.java

package cn.slimsmart.thrift.rpc;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import org.apache.commons.pool.impl.GenericObjectPool;import org.apache.thrift.TServiceClient;import org.apache.thrift.TServiceClientFactory;import org.springframework.beans.factory.FactoryBean;import org.springframework.beans.factory.InitializingBean;import cn.slimsmart.thrift.rpc.ThriftClientPoolFactory.PoolOperationCallBack;import cn.slimsmart.thrift.rpc.zookeeper.ThriftServerAddressProvider;/** * 客户端代理 */@SuppressWarnings({ "unchecked", "rawtypes" })public class ThriftServiceClientProxyFactory implements FactoryBean, InitializingBean {private Integer maxActive = 32;// 最大活跃连接数// ms,default 3 min,链接空闲时间// -1,关闭空闲检测private Integer idleTime = 180000;private ThriftServerAddressProvider serverAddressProvider;private Object proxyClient;private Class<?> objectClass;private GenericObjectPool<TServiceClient> pool;private PoolOperationCallBack callback = new PoolOperationCallBack() {@Overridepublic void make(TServiceClient client) {System.out.println("create");}@Overridepublic void destroy(TServiceClient client) {System.out.println("destroy");}};public void setMaxActive(Integer maxActive) {this.maxActive = maxActive;}public void setIdleTime(Integer idleTime) {this.idleTime = idleTime;}public void setServerAddressProvider(ThriftServerAddressProvider serverAddressProvider) {this.serverAddressProvider = serverAddressProvider;}@Overridepublic void afterPropertiesSet() throws Exception {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 加载Iface接口objectClass = classLoader.loadClass(serverAddressProvider.getService() + "$Iface");// 加载Client.Factory类Class<TServiceClientFactory<TServiceClient>> fi = (Class<TServiceClientFactory<TServiceClient>>) classLoader.loadClass(serverAddressProvider.getService() + "$Client$Factory");TServiceClientFactory<TServiceClient> clientFactory = fi.newInstance();ThriftClientPoolFactory clientPool = new ThriftClientPoolFactory(serverAddressProvider, clientFactory, callback);GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();poolConfig.maxActive = maxActive;poolConfig.minIdle = 0;poolConfig.minEvictableIdleTimeMillis = idleTime;poolConfig.timeBetweenEvictionRunsMillis = idleTime / 2L;pool = new GenericObjectPool<TServiceClient>(clientPool, poolConfig);proxyClient = Proxy.newProxyInstance(classLoader, new Class[] { objectClass }, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//TServiceClient client = pool.borrowObject();try {return method.invoke(client, args);} catch (Exception e) {throw e;} finally {pool.returnObject(client);}}});}@Overridepublic Object getObject() throws Exception {return proxyClient;}@Overridepublic Class<?> getObjectType() {return objectClass;}@Overridepublic boolean isSingleton() {return true;}public void close() {if (serverAddressProvider != null) {serverAddressProvider.close();}}}
下面我们看一下服务端和客户端的配置;

服务端spring-context-thrift-server.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"default-lazy-init="false"><!-- zookeeper --><bean id="thriftZookeeper" class="cn.slimsmart.thrift.rpc.zookeeper.ZookeeperFactory"destroy-method="close"><property name="zkHosts"value="192.168.36.54:2181,192.168.36.99:2181,192.168.36.189:2181" /><property name="namespace" value="cn.slimsmart.thrift.rpc.demo" /><property name="connectionTimeout" value="3000" /><property name="sessionTimeout" value="3000" /><property name="singleton" value="true" /></bean><bean id="sericeAddressRegister"class="cn.slimsmart.thrift.rpc.zookeeper.ThriftServerAddressRegisterZookeeper"destroy-method="close"><property name="zkClient" ref="thriftZookeeper" /></bean><bean id="echoSerivceImpl" class="cn.slimsmart.thrift.rpc.demo.EchoSerivceImpl" /><bean id="echoSerivce" class="cn.slimsmart.thrift.rpc.ThriftServiceServerFactory"destroy-method="close"><property name="service" ref="echoSerivceImpl" /><property name="port" value="9000" /><property name="version" value="1.0.0" /><property name="weight" value="1" /><property name="thriftServerAddressRegister" ref="sericeAddressRegister" /></bean><bean id="echoSerivce1" class="cn.slimsmart.thrift.rpc.ThriftServiceServerFactory"destroy-method="close"><property name="service" ref="echoSerivceImpl" /><property name="port" value="9001" /><property name="version" value="1.0.0" /><property name="weight" value="1" /><property name="thriftServerAddressRegister" ref="sericeAddressRegister" /></bean><bean id="echoSerivce2" class="cn.slimsmart.thrift.rpc.ThriftServiceServerFactory"destroy-method="close"><property name="service" ref="echoSerivceImpl" /><property name="port" value="9002" /><property name="version" value="1.0.0" /><property name="weight" value="1" /><property name="thriftServerAddressRegister" ref="sericeAddressRegister" /></bean></beans>
客户端:spring-context-thrift-client.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"default-lazy-init="false"><!-- fixedAddress --><!--<bean id="fixedAddressProvider" class="cn.slimsmart.thrift.rpc.zookeeper.FixedAddressProvider"> <property name="service" value="cn.slimsmart.thrift.rpc.demo.EchoSerivce" />         <property name="serverAddress" value="192.168.36.215:9001:1,192.168.36.215:9002:2,192.168.36.215:9003:3" /></bean>    <bean id="echoSerivce" class="cn.slimsmart.thrift.rpc.ThriftServiceClientProxyFactory">        <property name="maxActive" value="5" />        <property name="idleTime" value="10000" />        <property name="serverAddressProvider" ref="fixedAddressProvider" />    </bean>   -->    <!-- zookeeper   -->    <bean id="thriftZookeeper" class="cn.slimsmart.thrift.rpc.zookeeper.ZookeeperFactory"destroy-method="close"><property name="zkHosts"value="192.168.36.54:2181,192.168.36.99:2181,192.168.36.189:2181" /><property name="namespace" value="cn.slimsmart.thrift.rpc.demo" /><property name="connectionTimeout" value="3000" /><property name="sessionTimeout" value="3000" /><property name="singleton" value="true" /></bean>    <bean id="echoSerivce" class="cn.slimsmart.thrift.rpc.ThriftServiceClientProxyFactory" destroy-method="close">        <property name="maxActive" value="5" />        <property name="idleTime" value="1800000" />        <property name="serverAddressProvider">        <bean class="cn.slimsmart.thrift.rpc.zookeeper.ThriftServerAddressProviderZookeeper">        <property name="service" value="cn.slimsmart.thrift.rpc.demo.EchoSerivce" />        <property name="version" value="1.0.0" />        <property name="zkClient" ref="thriftZookeeper" />        </bean>        </property>    </bean></beans>
运行服务端后,我们可以看见zookeeper注册了多个服务地址。


详细实例这里就不详述了,请参考实例代码:https://github.com/slimina/thrift-zookeeper-rpc

关于Thrift设计优化文档:

Thrift RPC服务框架日志的优化

2 0
原创粉丝点击