thrift TProcessor
大家有没有想过,调用方调用一个方法,服务端是怎么找到对应方法并处理的?
很多RPC里是通过反射实现的,但是thrift里用的是另一种方式。
Server将 thrift 所有功能整合到一起:
1、创建一个 Transport;
2、创建 Transport 使用的 I/O Protocol;
3、为 I/O Protocol 创建 Processor;
4、启动服务,等待客户端的连接;
thrift不同语言实现提供的服务器端的模式不一样
thrift Java版本为服务器端提供了多种模式: TSimpleServer 、 TThreadPoolServer 、 TNonblockingServer 、 THsHaServer 、 TThreadedSelectorServer
Thrift Go版本为服务器端提供了 TSimpleServer
IO模型 | Java | Go | 特点 |
---|---|---|---|
阻塞IO | TSimpleServer | - | 只有一个工作线程,循环监听新请求的到来并完成对请求的处理,一次只能接收和处理一个socket连接,效率比较低。 |
阻塞IO | TThreadPoolServer | TSimpleServer | |
IO多路复用 | TNonblockingServer | - | TNonblockingServer 单线程工作,采用NIO的方式,所有的socket都被注册到selector中 |
TClient ,
TStandardClient、WrappedTClient 实现了TClient
接口的Call
方法
https://github.com/weikeqin/thrift-tutorial-go-demo
以client调用add方法为例
调用下游Add()方法代码
func main() {
// 创建thrift client
thriftClient := getThriftClient()
// 创建 calculatorClient tutorial是由idl生成的
calculatorClient := tutorial.NewCalculatorClient(thriftClient)
// 调用Add方法
sum, _ := calculatorClient.Add(defaultCtx, 1, 2)
fmt.Print("1+2=", sum, "\n")
}
// 获取一个thriftClient
func getThriftClient() *thrift.TStandardClient {
addr := flag.String("addr", "localhost:9090", "Address to listen to")
var transportFactory thrift.TTransportFactory
transportFactory = thrift.NewTTransportFactory()
cfg := &thrift.TConfiguration{
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
// 传输方式
var transport thrift.TTransport
transport = thrift.NewTSocketConf(*addr, cfg)
// 传输方式增强
transport, err := transportFactory.GetTransport(transport)
if err != nil {
fmt.Println(err)
}
//defer transport.Close()
if err := transport.Open(); err != nil {
fmt.Println(err)
}
// 协议工厂
var protocolFactory thrift.TProtocolFactory
protocolFactory = thrift.NewTBinaryProtocolFactoryConf(nil)
iprot := protocolFactory.GetProtocol(transport)
oprot := protocolFactory.GetProtocol(transport)
// 创建thrift client,
// 这个client使用TStandardClient类型, TStandardClient 实现了 TClient 接口
thriftClient := thrift.NewTStandardClient(iprot, oprot)
return thriftClient
}
传输方式(Transport)作为rpc框架接收报文的入口,提供各种底层实现如socket创建、读写、接收连接等。
同时实现各种复写传输层包括http、framed、buffered、压缩传输等。
thrift支持多种传输方式
传输方式 | 特点 |
---|---|
TSocket | 阻塞型 socket,用于客户端,采用系统函数 read 和 write 进行读写数据。 |
TServerSocket | 非阻塞型 socket,用于服务器端,accecpt 到的 socket 类型都是 TSocket(即阻塞型 socket)。 |
TBufferedTransport | |
TFramedTransport | |
TMemoryBuffer | |
TFileTransport | |
TFDTransport | |
TSimpleFileTransport | |
TZlibTransport | |
TSSLSocket | |
TSSLServerSocket |
Thrift是一个轻量、支持多语言、可扩展、高性能的远程服务调用框架。
提供了数据传输、序列化、应用层处理的清晰抽象。
The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.
分层 | 职责 |
---|---|
服务调用层 (客户端/服务端) | 客户端、服务端调用。 |
协议层 | 消息解析 |
传输层包装 | 功能增强,实现各种复写传输层包括http、framed、buffered、压缩传输等 |
低级传输层 | 靠近网络层、作为rpc框架接收报文的入口,提供各种底层实现如socket创建、读写、接收连接等。 |
语言层 | thrift采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发 |
操作系统层 | 由编程语言提供各种操作系统的支持 |
协议大家平时都会遇到,只是没有特别注意。
像平时大家阅读文章的时候都是从上到下、从左往右 按行阅读,这可以看做一种阅读协议。 ( 备注: 古人在竹简上写的文字则是从上往下、从右往左 按列阅读。)
更详细的规则比如:作文的第一行是标题,段首要空两格的是一个自然段,遇到一个句号是一句话。
详细的标点符号用法(通信协议)参考教育部的规范 标点符号用法 - 教育部
在计算机远程方法调用时,传输的都是二进制的01,调用方(写数据)和被调用方(读数据)怎么约定通信协议的?
协议的作用就类似于文字中的符号,作为应用拆解请求消息的边界,保证二进制数据经过网络传输后,还能被正确地还原语义。
具体点就是从二进制数据中解析出协议版本
、方法名
、消息类型
、序列Id
、序列化方式
、消息长度
、协议体
等内容。
Kafka是一个消息队列。被大家用在分布式消息队列或者流数据处理场景。
有几个问题,大家不知道思考过没有?
1、Kafka有哪些组件,作用分别是什么,为什么这么设计?
2、Kafka是怎么保证高性能、高并发、高可用的呢?
经常会遇到的一个问题是数据库如何保证不丢数据? 同样的Redis如何保证不丢数据?
MySQL里有 redo log、bin log、undo log
Redis包含 rdb log、aof log
在原来coding pages服务和github pages服务都可以免费试用时,在域名解析时配置 国内访问coding pages部署的博客静态文件,国外访问github pages部署的静态文件。国内国外访问速度都基本都在200ms内。
自从 coding.net 停止免费的pages服务后,只有github pages可以免费试用,国内访问博客只能访问github pages服务,导致访问速度很慢。被人吐槽了一次,所以想优化一下。
优化思路比较简单,
1、国内ip访问国内的博客服务 比如gitee pages服务
2、通过缓存加快访问速度 比如CDN缓存
在做系统稳定性-下游用户接口的兜底时,当时有2种方案:
第一种是只校验用户uid(18位数字 long类型 8字节)是否合法,这种方案实现简单,但有安全风险;
另一种方案是缓存一份用户信息(uid、城市、运营单元、收货地址id、身份 等) 数据,用户接口异常时用缓存数据校验,这种方案实现稍微复杂点,但是没有安全风险。
当时有2亿多用户,别的组的同学缓存过全量(20多个字段)用户数据,用了200G内存。 由于占用内存太多,收益不高,他们很快就放弃缓存用户数据了。
由于我们并不需要缓存用户的20多个字段,只需要缓存6个字段,想自己计算一下+实验下看需要多大内存,如果占用内存少可以使用方案二。