每种语言都有自己最擅长的领域,Golang 最适合的领域就是服务器端程序。

  做为服务器端程序,需要考虑性能同时也要考虑与各种语言之间方便的通讯。采用http协议简单,但性能不高。采用TCP通讯,则需要考虑封包、解包、粘包等等很多因素,而且想写个高效的TCP服务,也很难。

  其实,对于此类需求,采用RPC(Remote Procedure Call Protocol)编程最靠谱。使用 RPC 编程被认为是在分布式环境中运行的客户机和服务器应用程序之间进行可靠通信的最强大、最高效的方法之一。

  Golang内置了对RPC支持,但只能适用于go语言程序之间调用。如果go语言能使用Thrift开发,那么就如虎添翼了。可惜,thrift虽然很早就提供了golang的代码,但一直都存在各种问题无法正确执行……直到0.9.1版本的发布!

  最近,Apache Thrift 0.9.1正式发布了。新版的Thrift终于对Golang提供了完美的支持。经过实验,服务器端、客户端已经完美支持跨语言调用,且性能、尤其是内存占用上,编译型语言的特点展现出来,比java版的实现强了很多。

  下面,我们采用golang实现一个Thrift的Server端和Client端程序。

一、开发前准备

1、安装golang的Thrift包:

1 go get git.apache.org/thrift.git/lib/go/thrift

2、产生协议库:

  这是我定义的测试用IDL,为检验兼容性,采用了多种数据结构:

01 RpcService.thrift
02  
03 namespace go demo.rpc
04 namespace java demo.rpc
05  
06 // 测试服务
07 service RpcService {
08  
09     // 发起远程调用
10     list<string> funCall(1:i64 callTime, 2:string funCode, 3:map<string, string> paramMap),
11  
12 }

3、生成开发库

  下载开发库编译器 http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.1/thrift-0.9.1.exe

  thrift-0.9.1.exe  -gen go RpcService.thrift

  自动生成出的源码结构如下:

其中 constants.go、rpc_service.go、ttypes.go 是协议库,编写程序需要用到。rpc_service-remote.go 是自动生成的例程,可以不用。

二、go语言实现

1、服务器端

下面,我们来写服务器端程序:

01 package main
02  
03 import (
04     "demo/rpc"
05     "fmt"
06     "git.apache.org/thrift.git/lib/go/thrift"
07     "os"
08 )
09  
10 const (
11     NetworkAddr = "127.0.0.1:19090"
12 )
13  
14 type RpcServiceImpl struct {
15 }
16  
17 func (this *RpcServiceImpl) FunCall(callTime int64, funCode string, paramMap map[string]string) (r []string, err error) {
18     fmt.Println("-->FunCall:", callTime, funCode, paramMap)
19  
20     for k, v := range paramMap {
21         r = append(r, k+v)
22     }
23     return
24 }
25  
26 func main() {
27     transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
28     protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
29     //protocolFactory := thrift.NewTCompactProtocolFactory()
30  
31     serverTransport, err := thrift.NewTServerSocket(NetworkAddr)
32     if err != nil {
33         fmt.Println("Error!", err)
34         os.Exit(1)
35     }
36  
37     handler := &RpcServiceImpl{}
38     processor := rpc.NewRpcServiceProcessor(handler)
39  
40     server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
41     fmt.Println("thrift server in", NetworkAddr)
42     server.Serve()
43 }

  加空行也不过才43行,怎么样简单吧。

2、客户端程序

01 package main
02  
03 import (
04     "demo/rpc"
05     "fmt"
06     "git.apache.org/thrift.git/lib/go/thrift"
07     "net"
08     "os"
09     "time"
10 )
11  
12 func main() {
13     startTime := currentTimeMillis()
14     transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
15     protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
16  
17     transport, err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1", "19090"))
18     if err != nil {
19         fmt.Fprintln(os.Stderr, "error resolving address:", err)
20         os.Exit(1)
21     }
22  
23     useTransport := transportFactory.GetTransport(transport)
24     client := rpc.NewRpcServiceClientFactory(useTransport, protocolFactory)
25     if err := transport.Open(); err != nil {
26         fmt.Fprintln(os.Stderr, "Error opening socket to 127.0.0.1:19090", " ", err)
27         os.Exit(1)
28     }
29     defer transport.Close()
30  
31     for i := 0; i < 1000; i++ {
32         paramMap := make(map[string]string)
33         paramMap["name"] = "qinerg"
34         paramMap["passwd"] = "123456"
35         r1, e1 := client.FunCall(currentTimeMillis(), "login", paramMap)
36         fmt.Println(i, "Call->", r1, e1)
37     }
38  
39     endTime := currentTimeMillis()
40     fmt.Println("Program exit. time->", endTime, startTime, (endTime - startTime))
41 }
42  
43 // 转换成毫秒
44 func currentTimeMillis() int64 {
45     return time.Now().UnixNano() / 1000000
46 }

  分别编译,先启动服务器端,然后在执行客户端程序。可以看到控制台正确打印出了信息,说明调用通过。

-->FunCall: 1380446325199 login map[name:qinerg passwd:123456]

三、Java版实现

  为了验证跨语言调用,下面我们分别再用java实现一下服务器端和客户端:

  生成Java版开发库:

  thrift-0.9.1.exe  -gen java RpcService.thrift

1、Java服务器版

01 package demo.rpc;
02  
03 import java.util.ArrayList;
04 import java.util.List;
05 import java.util.Map;
06 import java.util.Map.Entry;
07  
08 import org.apache.thrift.TException;
09 import org.apache.thrift.protocol.TBinaryProtocol;
10 import org.apache.thrift.protocol.TBinaryProtocol.Factory;
11 import org.apache.thrift.server.TNonblockingServer;
12 import org.apache.thrift.server.TServer;
13 import org.apache.thrift.transport.TNonblockingServerSocket;
14 import org.apache.thrift.transport.TNonblockingServerTransport;
15 import org.apache.thrift.transport.TTransportException;
16  
17 /**
18  * Thrift测试服务器
19  */
20 public class Server implements RpcService.Iface {
21  
22     public static void main(String[] as) {
23         TNonblockingServerTransport serverTransport = null;
24         try {
25             serverTransport = new TNonblockingServerSocket(19090);
26         } catch (TTransportException e) {
27             e.printStackTrace();
28         }
29  
30         RpcService.Processor<RpcService.Iface> processor = new RpcService.Processor<RpcService.Iface>(
31                 new Server());
32  
33         Factory protFactory = new TBinaryProtocol.Factory(true, true);
34         //TCompactProtocol.Factory protFactory = new TCompactProtocol.Factory();
35  
36         TNonblockingServer.Args args = new TNonblockingServer.Args(
37                 serverTransport);
38         args.processor(processor);
39         args.protocolFactory(protFactory);
40         TServer server = new TNonblockingServer(args);
41         System.out.println("Start server on port 19090 ...");
42         server.serve();
43     }
44  
45     @Override
46     public List<String> funCall(long callTime, String funCode,
47             Map<String, String> paramMap) throws TException {
48         System.out.println("-->FunCall:" + callTime + " " + funCode + " "
49                 + paramMap);
50         List<String> retList = new ArrayList<>();
51  
52         for (Entry<String, String> entry : paramMap.entrySet()) {
53             retList.add(entry.getKey() + entry.getValue());
54         }
55  
56         return retList;
57     }
58 }

2、Java客户端版

01 package demo.rpc;
02  
03 import java.util.HashMap;
04 import java.util.Map;
05  
06 import org.apache.thrift.TException;
07 import org.apache.thrift.protocol.TBinaryProtocol;
08 import org.apache.thrift.transport.TFramedTransport;
09 import org.apache.thrift.transport.TSocket;
10 import org.apache.thrift.transport.TTransport;
11  
12 /**
13  * Thrift测试客户端
14  */
15 public class Client {
16  
17     public static void main(String[] args) {
18          
19         long startTime = System.currentTimeMillis();
20         try {
21             TTransport transport = new TFramedTransport(new TSocket("localhost", 19090));
22              
23             TBinaryProtocol protocol = new TBinaryProtocol(transport);
24             //TCompactProtocol protocol = new TCompactProtocol(transport);
25              
26             RpcService.Client client = new RpcService.Client(protocol);
27             transport.open();
28              
29             Map<String, String> param = new HashMap<String, String>();
30             param.put("name", "qinerg");
31             param.put("passwd", "123456");
32              
33             for (int i = 0; i < 1000; i++) {
34                 System.out.println(client.funCall(System.currentTimeMillis() , "login", param));
35             }
36              
37             transport.close();
38         } catch (TException x) {
39             x.printStackTrace();
40         }
41         long endTime = System.currentTimeMillis();
42         System.out.println(" 本次调用完成时间:" + endTime + "   " + startTime + "  " + (endTime - startTime));
43     }
44 }

  好了,现在启动java版服务器端,用golang版客户端调用,完全没有问题。启动golang版服务器端程序,用java版客户端调用,同样OK。

  完美实现了跨语言调用。

Golang通过Thrift框架完美实现跨语言调用的更多相关文章

  1. Golang、Php、Python、Java基于Thrift0.9.1实现跨语言调用

    目录: 一.什么是Thrift? 1) Thrift内部框架一瞥 2) 支持的数据传输格式.数据传输方式和服务模型 3) Thrift IDL 二.Thrift的官方网站在哪里? 三.在哪里下载?需要 ...

  2. 使用thrift进行跨语言调用(php c# java)

    使用thrift进行跨语言调用(php c# java)   1:前言 实际上本文说的是跨进程的异构语言调用,举个简单的例子就是利用PHP写的代码去调C#或是java写的服务端.其实除了本文提供的办法 ...

  3. Atitit java c# php c++ js跨语言调用matlab实现边缘检测等功能attilax总结

    Atitit java c# php c++ js跨语言调用matlab实现边缘检测等功能attilax总结 1.1. 边缘检测的基本方法Canny最常用了1 1.2. 编写matlab边缘检测代码, ...

  4. 跨语言调用Hangfire定时作业服务

    跨语言调用Hangfire定时作业服务 背景 Hangfire允许您以非常简单但可靠的方式执行后台定时任务的工作.内置对任务的可视化操作.非常方便. 但令人遗憾的是普遍都是业务代码和hagnfire服 ...

  5. C++ 跨语言调用 Java

    C++ 跨语言调用 Java Java JDK 提供了 JNI 接口供 C/C++ 程序调用 Java 编译后的类与方法,主要依赖于头文件(jni.h) 和 动态库(jvm.so/jvm.dll),由 ...

  6. vs2019 Com组件初探-简单的COM编写以及实现跨语言调用

    前提条件 1.掌握C++基础语法 2.平台安装 vs2019 3.本地平台为 windows 10 1909 X64 4.了解vbs基础语法 本次目标 1.掌握Com组件的概念及原理 2.编写一个简单 ...

  7. Java跨语言调用,使用JNA访问Java外部接口

    1. JNA简单介绍 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即 ...

  8. CLS(公共语言规范)的CLSCompliant(跨语言调用)

    .net的一个很重要的特性就是跨语言的编程,用C#写的dll可以在VB.net里调用,例如:用C#写的一个类,编译到dll中,然后在VB.net中调用: using System;namespace  ...

  9. 跨语言调用C#代码的新方式-DllExport

    简介 上一篇文章使用C#编写一个.NET分析器文章发布以后,很多小伙伴都对最新的NativeAOT函数导出比较感兴趣,今天故写一篇短文来介绍一下如何使用它. 在以前,如果有其他语言需要调用C#编写的库 ...

随机推荐

  1. linux下查看cpu物理个数、核数、逻辑cpu数

    一.首先要明确物理cpu个数.核数.逻辑cpu数的概念 1.物理cpu数:主板上实际插入的cpu数量,可以数不重复的 physical id 有几个(physical id) 2.cpu核数:单块CP ...

  2. Modern C++ CHAPTER 2(读书笔记)

    CHAPTER 2 Recipe 2-1. Initializing Variables Recipe 2-2. Initializing Objects with Initializer Lists ...

  3. flask-admin章节一:使用chartkick画报表

    一般中小型WEB整体来看逻辑比较简单些,一般都是基于数据库的增删改查.不过通过数据库查询到的记录直接展示给用户不是很直观,大家其实蛮期待有一个报表 直接展示他们期待的内容. 这块就涉及到数据的提取和展 ...

  4. mysql.connector操作mysql的blob值

    This tutorial shows you how to work with MySQL BLOB data in Python, with examples of updating and re ...

  5. vSphere6提示已弃用VMFS卷的解决方法

    Deprecated VMFS volume(s) found on the host. Please consider upgrading volume(s) to the latest versi ...

  6. 性能测试之--Apache JMeter安装

    Hi,今天给大家分享一下安装JMeter的相关内容~ Apache JMeter 是Apache组织的开源项目,是一个100%纯Java桌面应用,用于压力测试和性能测试. 它能够对HTTP.FTP服务 ...

  7. 模拟n个人参加选举的过程,并输出选举结果:假设候选人有四人,分别用A,B,C,D表示,当选某候选人时,直接输入其编号(编号由计算机随机产生,若输入的不是A,B,C,D则视为无效票,选举结束后按得票数从高到底输出候选人编号和所得票数.

    模拟n个人参加选举的过程,并输出选举结果:假设候选人有四人,分别用A,B,C,D表示,当选某候选人时,直接输入其编号(编号由计算机随机产生,若输入的不是A,B,C,D则视为无效票,选举结束后按得票数从 ...

  8. hdoj 2039 三角形

    Problem Description 给定三条边,请你判断一下能不能组成一个三角形.   Input 输入数据第一行包含一个数M,接下有M行,每行一个实例,包含三个正数A,B,C.其中A,B,C & ...

  9. android studio 华为手机看不到具体的错误日志

    手机的开发人员选项打开了么,其中的 USB 调试打开了么?搞定他们并重新运行,是否能找到我们的日志?否,转到 2. 日志中是否有这样 could not disable core file gener ...

  10. 说说Python中的闭包 - Closure

    转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...