Thrift-client与spring集成

SpringBoot集成Thrift 前言:编写一个微服务,即能提供http调用,也可提供rpc调用,并且在rpc调用时采用pb进行序列化。 一、项目结构 thrift-test - .idea - pom.xml - thrift-client - src - thrift-server server端提供服务,client端进行调用 二、Server端 2.1 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://m 阅读详情

    Thrift-client作为服务消费端,由于thrift使用socket通讯,因此它需要面对几个问题:

    1) client端需要知道server端的IP + port,如果是分布式部署,还需要知道所有server的IP + port列表.  

    2) client为了提升性能,不可能只使用一个socket来处理并发请求,当然也不能每个请求都创建一个socket;我们需要使用连接池方案.

    3) 对于java开发工程师而言,基于spring配置thrift服务,可以提供很多的便利.

    4) 基于zookeeper配置管理,那么client端就不需要"硬编码"的配置server的ip + port,可以使用zookeeper来推送每个service的服务地址.

    5) 因为thrift-client端不使用连接池的话,将不能有效的提高并发能力,本文重点描述看如何使用thrift-client连接池。

 

1. pom.xml

<dependencies>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
		<version>3.0.7.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.apache.zookeeper</groupId>
		<artifactId>zookeeper</artifactId>
		<version>3.4.5</version>
		<!--<exclusions>-->
			<!--<exclusion>-->
				<!--<groupId>log4j</groupId>-->
				<!--<artifactId>log4j</artifactId>-->
			<!--</exclusion>-->
		<!--</exclusions>-->
	</dependency>
	<!--
	<dependency>
		<groupId>com.101tec</groupId>
		<artifactId>zkclient</artifactId>
		<version>0.4</version>
	</dependency>
	-->
	<dependency>
		<groupId>org.apache.thrift</groupId>
		<artifactId>libthrift</artifactId>
		<version>0.9.1</version>
	</dependency>
	<dependency>
		<groupId>org.apache.curator</groupId>
		<artifactId>curator-recipes</artifactId>
		<version>2.3.0</version>
	</dependency>
	<dependency>
		<groupId>commons-pool</groupId>
		<artifactId>commons-pool</artifactId>
		<version>1.6</version>
	</dependency>

</dependencies>

2. spring-thrift-client.xml

    其中zookeeper作为可选项,开发者也可以通过制定serverAddress的方式指定server的地址.

<!-- fixedAddress -->
<!--  
<bean id="userService" class="com.demo.thrift.ThriftServiceClientProxyFactory">
	<property name="service" value="com.demo.service.UserService"></property>
	<property name="serverAddress" value="127.0.0.1:9090:2"></property>
	<property name="maxActive" value="5"></property>
	<property name="idleTime" value="10000"></property>
</bean>
-->
<!-- zookeeper -->
<bean id="thriftZookeeper" class="com.demo.thrift.zookeeper.ZookeeperFactory" destroy-method="close">
	<property name="connectString" value="127.0.0.1:2181"></property>
	<property name="namespace" value="demo/thrift-service"></property>
</bean>
<bean id="userService" class="com.demo.thrift.ThriftServiceClientProxyFactory" destroy-method="close">
	<property name="service" value="com.demo.service.UserService"></property>
	<property name="maxActive" value="5"></property>
	<property name="idleTime" value="1800000"></property>
	<property name="addressProvider">
		<bean class="com.demo.thrift.support.impl.DynamicAddressProvider">
			<property name="configPath" value="UserServiceImpl"></property>
			<property name="zookeeper" ref="thriftZookeeper"></property>
		</bean>
	</property>
</bean>

3. ThriftServiceClientProxyFactory.java

    因为我们要在client端使用连接池方案,那么就需要对client的方法调用过程,进行代理,这个类,就是维护了一个"Client"代理类,并在方法调用时,从"对象池"中取出一个"Client"对象,并在方法实际调用结束后归还给"对象池".  

@SuppressWarnings("rawtypes")
public class ThriftServiceClientProxyFactory implements FactoryBean,InitializingBean {

    private String service;

    private String serverAddress;
    
    private Integer maxActive = 32;//最大活跃连接数
    
    ////ms,default 3 min,链接空闲时间
    //-1,关闭空闲检测
    private Integer idleTime = 180000;
    private ThriftServerAddressProvider addressProvider;

	private Object proxyClient;
	

    public void setMaxActive(Integer maxActive) {
		this.maxActive = maxActive;
	}


	public void setIdleTime(Integer idleTime) {
		this.idleTime = idleTime;
	}


	public void setService(String service) {
		this.service = service;
	}


    public void setServerAddress(String serverAddress) {
        this.serverAddress = serverAddress;
    }


    public void setAddressProvider(ThriftServerAddressProvider addressProvider) {
		this.addressProvider = addressProvider;
	}

	private Class objectClass;
    
    private GenericObjectPool<TServiceClient> pool;
    
    private PoolOperationCallBack callback = new PoolOperationCallBack() {
		
		@Override
		public void make(TServiceClient client) {
			System.out.println("create");
			
		}
		
		@Override
		public void destroy(TServiceClient client) {
			System.out.println("destroy");
			
		}
	};

    @Override
    public void afterPropertiesSet() throws Exception {
        if(serverAddress != null){
            addressProvider = new FixedAddressProvider(serverAddress);
        }
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //加载Iface接口
        objectClass = classLoader.loadClass(service + "$Iface");
        //加载Client.Factory类
        Class<TServiceClientFactory<TServiceClient>> fi = (Class<TServiceClientFactory<TServiceClient>>)classLoader.loadClass(service + "$Client$Factory");
        TServiceClientFactory<TServiceClient> clientFactory = fi.newInstance();
        ThriftClientPoolFactory clientPool = new ThriftClientPoolFactory(addressProvider, 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() {
            @Override
            public 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);
            	}
            }
        });
    }

    @Override
    public Object getObject() throws Exception {
        return proxyClient;
    }

    @Override
    public Class<?> getObjectType() {
        return objectClass;
    }

    @Override
    public boolean isSingleton() {
        return true;  //To change body of implemented methods use File | Settings | File Templates.
    }
    
    public void close(){
    	if(addressProvider != null){
    		addressProvider.close();
    	}
    }
}

4. ThriftClientPoolFactory.java

    "Client"对象池,对象池中是已经实例化的Client对象,Client对象负责与Thrift server通信.

/**
 * 连接池,thrift-client for spring
 */
public class ThriftClientPoolFactory extends BasePoolableObjectFactory<TServiceClient>{

    private final ThriftServerAddressProvider addressProvider;
    
    private final TServiceClientFactory<TServiceClient> clientFactory;
    
    private PoolOperationCallBack callback;

    protected ThriftClientPoolFactory(ThriftServerAddressProvider addressProvider,TServiceClientFactory<TServiceClient> clientFactory) throws Exception {
        this.addressProvider = addressProvider;
        this.clientFactory = clientFactory;
    }
    
    protected ThriftClientPoolFactory(ThriftServerAddressProvider addressProvider,TServiceClientFactory<TServiceClient> clientFactory,PoolOperationCallBack callback) throws Exception {
        this.addressProvider = addressProvider;
        this.clientFactory = clientFactory;
        this.callback = callback;
    }



    @Override
    public TServiceClient makeObject() throws Exception {
        InetSocketAddress address = addressProvider.selector();
        TSocket tsocket = new TSocket(address.getHostName(),address.getPort());
        TProtocol protocol = new TBinaryProtocol(tsocket);
        TServiceClient client = this.clientFactory.getClient(protocol);
        tsocket.open();
        if(callback != null){
        	try{
        		callback.make(client);
        	}catch(Exception e){
        		//
        	}
        }
        return 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();
    }
    
    static interface PoolOperationCallBack {
    	//销毁client之前执行
    	void destroy(TServiceClient client);
    	//创建成功是执行
    	void make(TServiceClient client);
    }

}

5. DynamicAddressProvider.java

    将zookeeper作为server地址的提供者,这样客户端就不需要再配置文件中指定一堆ip + port,而且当server服务有更新时,也不需要client端重新配置.

/**
 * 可以动态获取address地址,方案设计参考
 * 1) 可以间歇性的调用一个web-service来获取地址
 * 2) 可以使用事件监听机制,被动的接收消息,来获取最新的地址(比如基于MQ,nio等)
 * 3) 可以基于zookeeper-watcher机制,获取最新地址
 * <p/>
 * 本实例,使用zookeeper作为"config"中心,使用apache-curator方法库来简化zookeeper开发
 * 如下实现,仅供参考
 */
public class DynamicAddressProvider implements ThriftServerAddressProvider, InitializingBean {

    private String configPath;

    private PathChildrenCache cachedPath;

    private CuratorFramework zookeeper;
    
    //用来保存当前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_PRIORITY = 1;

    public void setConfigPath(String configPath) {
        this.configPath = configPath;
    }

    public void setZookeeper(CuratorFramework zookeeper) {
        this.zookeeper = zookeeper;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
    	//如果zk尚未启动,则启动
    	if(zookeeper.getState() == CuratorFrameworkState.LATENT){
    		zookeeper.start();
    	}
        buildPathChildrenCache(zookeeper, configPath, true);
        cachedPath.start(StartMode.POST_INITIALIZED_EVENT);
    }

    private void buildPathChildrenCache(CuratorFramework client, String path, Boolean cacheData) throws Exception {
        cachedPath = new PathChildrenCache(client, path, cacheData);
        cachedPath.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                PathChildrenCacheEvent.Type eventType = event.getType();
                switch (eventType) {
//                    case CONNECTION_RECONNECTED:
//                        
//                        break;
                    case CONNECTION_SUSPENDED:
                    case CONNECTION_LOST:
                        System.out.println("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();
                    System.out.println("thrift server-cluster error....");
                    return;
                }
                List<InetSocketAddress> current = new ArrayList<InetSocketAddress>();
                for (ChildData data : children) {
                    String address = new String(data.getData(), "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 priority = DEFAULT_PRIORITY;
        if (hostname.length == 3) {
            priority = Integer.valueOf(hostname[2]);
        }
        String ip = hostname[0];
        Integer port = Integer.valueOf(hostname[1]);
        List<InetSocketAddress> result = new ArrayList<InetSocketAddress>();
        for (int i = 0; i < priority; i++) {
        	result.add(new InetSocketAddress(ip, port));
        }
        return result;
    }


    @Override
    public List<InetSocketAddress> getAll() {
        return Collections.unmodifiableList(container);
    }

    @Override
    public 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();//null
    }


    @Override
    public void close() {
        try {
            cachedPath.close();
            zookeeper.close();
        } catch (Exception e) {
            //
        }
    }
}

    到此为止,我们的Thrift基本上就可以顺利运行起来了.更多代码,参见附件.

    Thrift-server端开发与配置,参见[Thrift-server] 

  

spring与thrift集成 http://my.oschina.net/penngo/blog/492253文章源码 立即下载

相关推荐

基于立创开源ESP32步进电机驱动板硬件设计与优化实践

本文详细介绍了基于立创开源平台的ESP32步进电机驱动板硬件设计与优化实践。重点解析了ESP32-PICO-D4与A4950驱动芯片的选型搭配、电源电路噪声隔离、PCB布局布线技巧以及保护电路设计,为开发者提供了从原理图到PCB的完整优化方案,有效提升驱动板的性能与稳定性。

7z8x2c4v6b9的博客 647

Thrift和Spring集成

1.服务端通过TMultiplexedProcessor同时暴露多个服务 /** * Thrift通用服务启动类 可以和spring集成 */ public class CommonServerDemo { private int port; private MapString, Object> exposedServerMap; public Common

peng.zhou 1394

【计算摄影】相机成像原理:从光到JPEG图像

本文我们将从最基本的相机成像原理开始论述,再讨论光线进入相机后,经过一系列处理成为JPEG图像的大致流程。

qq_35149975的博客 2万+

java thrift spring_[转载] Thrift-server与spring集成

转载自http://shift-alt-ctrl.iteye.com/blog/1990026Thrift服务server端,其实就是一个ServerSocket线程 + 处理器,当Thrift-client端建立链接之后,处理器负责解析socket流信息,并根据其指定的"方法名"+参数列表,来调用"服务实现类"的方法,并将执行结果(或者异常)写入到socket中.一个server,就需要创建一个...

weixin_42117622的博客 177

正负数据如何归一化_生信干货 | LC-MS代谢组学QI软件下机数据代谢物筛选和归一化如何做?...

上海·2019第一届多层组学前言技术发展华东区客户研讨会倒计时1天LC-MS非靶向代谢组学数据下机啦~~拿到一堆数据怎么办?不会高端代码,也不会自己写脚本,我怎么处理数据?​本期小编带来了LC-MS非靶向代谢组学下机数据处理实用干货,包括:下机定性结果表格的解析、代谢物筛选如何做?以及定量数据如何做归一化?实用干货,赶紧来抢~~话不多说,赶紧来学习吧~~非靶向LC-MS代谢组学的分析流程LC-MS...

weixin_36441329的博客 2983

如何在iPhone上本地运行SmolVLM?5亿参数模型实战指南(附Swift代码)

本文详细介绍了如何在iPhone上本地部署和运行轻量高效的SmolVLM多模态模型。通过实战指南,分享了使用Swift和MLX框架进行环境配置、模型加载、内存优化以及实现实时图像分析与文本生成的完整流程,帮助开发者在移动端高效运行AI视觉语言模型。

echo99的专栏 654

Spring集成Thrift,实现服务端和客户端代理

基于反射实现Thrift的服务注册和客户端调用的代理端,并结合前几节的HelloWorld接口、实现类以及连接池。

任何技能都是从模仿开始,逐步升华。 1万+

【AI智能体】Dify 搭建个人专属简历美化助手操作详解

Dify 搭建个人专属简历诊断助手实战操作详解

congge 4381

java thrift spring_spring与thrift集成

spring与thrift集成,可以使服务调用和发布更方便。本文代码是在上篇基础上改进,部分代码介绍请参考上一篇Thrift的java和php数据交互(http://my.oschina.net/penngo/blog/489311)服务器端spring配置xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="ht...

weixin_31439907的博客 355

Sparse-ICP—(4) 加权稀疏迭代最近点算法(matlab版)

本文介绍了加权稀疏迭代最近点算法的实现。

点云侠的博客 642

英飞凌TC3xx--深度手撕HSM安全启动(三)--TC3xx HSM系统架构

TC3xx的HSM内核是一颗32位的ARM Cortex M3,主频可达100MHz,支持对称算法AES128、非对称算法PKC(Public Key Crypto) ECC256、Hash SHA2,以及TRNG(真随机数生成)。

认真搞搞汽车MCU 5746

Spring集成Thrift--Server AND Client

http://hanqunfeng.iteye.com/blog/1936556 Thrift网上有N多教程,不再赘述,这里搭建的是WEB项目,使用了spring,所以尽量使用了基于配置的方式。 一。server端 本着少些代码,配置优先的原则,在server端引入代理类,如下: ThriftServerProxy:使用了反射   Java代码  

走马观花 1772

Microsemi Libero系列教程(全网首发)

关于Libero IDE和Libero SoC Libero分为Libero IDE和Libero SoC,Libero IDE最新版本是v9.2 SP3(2016年4月4日)已经不再更新维护了,而Libero SoC是Libero IDE的升级版,官方推荐的开发工具,文中的Libero指的是Libero SoC。 Libero SoC Libero® SoC是Microsemi(美高森美)官方...

whik1194的博客 2万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Thrift-client与Spring集成,并使用连接池提高新能

参考:  http://shift-alt-ctrl.iteye.com/blog/1990030?utm_source=tuicool&utm_medium=referral     Thrift-client作为服务消费端,由于thrift使用socket通讯,因此它需要面对几个问题:     1) client端需要知道server端的IP + port,如果

muyuxuebao的博客 9816

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

java thrift spring_springboot与thrift集成实现服务端和客户端

我们这里用一个简单的小功能来演示一下如何使用springboot集成thrift这个功能是,判断hdfs路径存在。1、先解决依赖org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-configurat...

weixin_35399228的博客 405

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

thrift zookeeper学习

转载于 基于ZooKeeper和Thrift构建动态RPC调用 一、基本功能   实现服务端向ZooKeeper集群注册自己提供的服务,并且把自己的IP地址和服务端口创建到具体的服务目录下。客户端向ZooKeeper集群监听自己关注的RPC服务(例如:sayHello和天气服务), 监听服务目录下的IP地址列表变化。 对于spring boot2的有关用法,目前还不是很熟悉。在尝试将此项目,转为s...

yangyangrenren的专栏 574

Thrift 个人实战--Thrift 服务化 Client的改造

前言:  Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还是有一定距离, 本系列将对Thrift作代码解读和框架扩充, 使得它更加贴近生产环境. 本文主要讲解thrift的服务化改造, 这边侧重于阐述对client(服务调用方)的改造和设计思想. ...

weixin_30628077的博客 101

整合Spring Boot和Thrift 实现RPC远程调用

这样就完成了Spring Boot和Thrift的整合,实现了RPC远程调用。整合Spring Boot和Thrift可以用于实现RPC远程调用。配置Thrift Server和Thrift Client的相关属性。在Controller中注入服务消费者。使用Thrift命令生成Java代码。添加Thrift Maven依赖。配置Thrift Server。配置Thrift Client。定义Thrift接口文件。

weixin_43981222的博客 725

Thrift连接池实现

简介Thrift是Facebook的核心框架之一,使不同的开发语言开发的系统可以通过该框架实现彼此的通信,类似于webservice,但是Thrift提供了近乎变态的效率和开发的方便性,是webservice所不能比拟的。给分布式开发带来了极大的方便。但是这柄利器也有一些不完美。 问题首先文档相当的少,只有一个wiki网站提供相应的帮助。这对于Thrift的推广极为不利。其次框架本身实现有一些缺陷...

流浪的代码 166

Thrift 基于zookeeper改造模式

对于Thrift服务化的改造,主要是客户端,可以从如下几个方面进行: 1.服务端的服务注册,客户端自动发现,无需手工修改配置,这里我们使用zookeeper,但由于zookeeper本身提供的客户端使用较为复杂,因此采用curator-recipes工具类进行处理服务的注册与发现。 2.客户端使用连接池对服务调用进行管理,提升性能,这里我们使用Apache Commons项目comm...

aikuangchou2298的博客 296

ZAP脚本引擎教程:如何用JavaScript和Python扩展扫描规则与拦截HTTP请求

ZAP(Zed Attack Proxy,由 Checkmarx 开发)是一款开源的 Web 应用安全扫描工具,而 **ZAP 脚本引擎**是它的"外挂接口":你可以用 JavaScript、Python、Kotlin 等语言编写脚本,让 ZAP 拦截并修改每一个 HTTP 请求/响应,甚至自定义被动扫描、主动扫描规则。本教程带你从零理解脚本类型、创建第一个脚本,到实现请求拦截与自定义扫描规则。

gitblog_00832的博客 725
上一篇: Apache curator-frameworker简述
下一篇: Mysql索引失效(备忘)
shift_alt_ctrl
博客等级 码龄10年 0粉丝 83原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值