- 工作目录: 约定使用
~/goprojects/- mkdir -p rpc_proxy/src/git.chunyu.me/infra/
- cd
rpc_proxy/src/git.chunyu.me/infra/ - git clone git@git.chunyu.me:infra/rpc_proxy.git
- IDE(
Pycharm + Go plugin)- https://github.com/go-lang-plugin-org/go-lang-idea-plugin
- 在最新的Pycharm中安装:
- https://plugins.jetbrains.com/plugin/5047 (版本: 0.9.748)
- 配置:
- 在Pycharm的 Preferences 中选择:
- Languages & Frameworks
- 选择go
- 设置Go SDK(设置GORoot)
- 选择Go Libraries, 选择Global Libraries(忽略), 使用Project libraries)
- 输入
source start_env.sh脚本中输出的地址,例如:- /Users/feiwang/goprojects/rpc_proxy
- 输入
- 在Pycharm的 Preferences 中选择:
相关的子项目:
Rpc Proxy分为4层,从前端往后端依次标记为L1, L2, L3, L4
- 最前端的RPC Client, 主要由thrift中间语言生成代码
- 上面思路: 我们修改了transport&protocol两个模块,通过简单的修改得到了python版本的zerothrift
- Java, Go等也能很方面地实现自己的RPC Client
- 其他语言只要支持Thrift和ZeroMq, 都可以方便地实现自己的Client
# Transport层是所有的RPC服务都共用的(除非在同一个请求内部实现了并发) _ = get_base_protocol(settings.RPC_LOCAL_PROXY) protocol = get_service_protocol("typo") from cy_typo.services.TypoService import Client _typo_client = Client(protocol) # 函数调用 rpc_result = _typo_client.correct_typo(content) content = rpc_result.fixed # 或者使用Pool pool = ConnectionPool(10, "127.0.0.1:5550") with pool.base_protocol() as base_protocol: protocol = get_service_protocol("typo", base_protocol) client = Client(protocol)
- 相应的Python RPC Client&Server的实现: https://github.com/wfxiang08/zerothrift
- 由于Python的进程功能太弱,不变在内部实现连接池等;如果让它们直接直连后端的Server, 则整个逻辑(connections)会非常乱,端口管理也会非常麻烦
- 服务的发现和负载均衡放在Client端赖做也会极大地增加了Client端的开发难度和负担
- Local Proxy层以产品为单位,负责发现所有的服务,并和L1层交互,让L1层不用关心服务部署的路径
- Proxy层也有简单的负载均衡的处理
# Go中的deamon似乎不太容易实现,借助: nohup &可以实现类似的效果(Codis也如此) # 默认的proxy(绑定: 127.0.0.0:5550) nohup rpc_proxy -c config.ini -L proxy.log >/dev/null 2>&1 & # 在测试服务器上部署,给大家开发测试使用的proxy nohup rpc_proxy -c config_test.ini -L proxy_test.log >/dev/null 2>&1 &
- Java/Go等天然支持多线程等,基本上不需要负载均衡, 因此这一层主要面向python
- 负责管理后端的Server, 自动进行负载均衡,以及处理后端服务的关闭,重启等异常情况
- 负责服务的注册
- 如果服务的正常关闭,会提前通知L2层,让L2层控制流量不再进入L3层的当前节点
- 如果服务异常关闭,则5s左右, L2层就会感知,并且下线对应的节点
## 配置文件 config.ini zk=rd1:2181,rd2:2181,mysql2:2181 # 线上的product一律以: online开头 # 而测试的product禁止使用 online的服务 product=test verbose=0 zk_session_timeout=30 ## Load Balance service=typo front_host= front_port=5555 back_address=127.0.0.1:5556 # 使用网络的IP, 如果没有指定front_host, 则使用使用当前机器的内网的Ip来注册 ip_prefix=10. ## Server worker_pool_size=2 ## Client/Proxy proxy_address=127.0.0.1:5550 # Go中的deamon似乎不太容易实现,借助: nohup &可以实现类似的效果(Codis也如此) nohup rpc_lb -c config.ini -L lb.log >/dev/null 2>&1 &
- 对于go, java直接在这一层提供服务
func main() { proxy.RpcMain(BINARY_NAME, SERVICE_DESC, // 默认的ThriftServer的配置checker proxy.ConfigCheckThriftService, // 可以根据配置config来创建processor func(config *utils.Config) proxy.Server { // 可以根据配置config来创建processor alg.InitGeoFix(FILE_GPS_2_SOGOU) processor := gs.NewGeoLocationServiceProcessor(&alg.GeoLocationService{}) return proxy.NewThriftRpcServer(config, processor) }) }
- 对于Python, rpc_proxy python框架即可
- 通过thrift idl生成接口, Processor等
- 实现Processor的接口
- 合理地配置参数
- 例如: worker_pool_size:如果是Django DB App则worker_pool_size=1(不支持异步数据库,多并发没有意义,即便支持异步数据库,Django的数据库逻辑也不支持高并发); 但是如果不使用DB, 那么Django还是可以支持多并发的
- 注意在iptables中开启相关的端口
- thrift只支持utf8格式的字符串,因此在使用过程中注意
- 字符串如果是unicode, 一方面len可能和utf8的 len不一样,另一方面,数据在序列化时可能报编码错误
class TypoProcessor(object): def correct_typo(self, query): # print "Query: ", query result = TypoResult() new_query, mappings = typo_manager.correct_typo(query) result.fixed = new_query result.fixes = [] for old, new in mappings: result.fixes.append(FixedTerm(old, new)) return result config_path = "config.ini" config = parse_config(config_path) endpoint = config["back_address"] service = config["service"] worker_pool_size = int(config["worker_pool_size"]) processor = Processor(TypoProcessor()) s = RpcWorker(processor, endpoint, pool_size=worker_pool_size, service=service) s.connect(endpoint) s.run()
- 编译:
- cd ~/goprojects/rpc_proxy/src/git.chunyu.me/infra/rpc_proxy
- 运行编译脚本:
bash scripts/build_lb.sh- go build cmds/rpc_lb.go
- 编译脚本和直接编译相比,将当前代码的版本,编译时间等打包到最终的文件中
bash scripts/build_proxy.sh
- 运维
- scp rpc_* node:/usr/local/rpc_proxy/bin/
- sudo cp rpc_* /usr/local/rpc_proxy/bin/
- 然后control_proxy.sh脚本和control_lb.sh脚本也需要部署在合适的地方
- 注意 scripts目录下的脚本