• python与consul 实现gRPC服务注册-发现


    背景

    通过对gRPC的介绍我们知道,当正常启动服务后,我们只需要知道ip,port就可以进行gRPC的连接。可以想到,这种方式并不适合用于线上环境,因为这样直连的话就失去了扩展性,当需要多机部署的时候,就无法在线上环境直接使用,而且当线上项目连接的那台服务器宕了的话,整个项目也会出错,这并不是我们想要的结果。
    于是,我们需要一个服务注册与发现的机制。也就是说当我们的rpc服务启动的时候注册到另一个服务器,然后客户端连接的时候去查找对应的服务,得到相应的ip,port,然后就可以顺利进行连接了。这种方式也就是服务注册与发现,目前有zoomkeper, consul, 因为自己对zookeeper不熟悉,所以这里选用consul。整个流程如图所示

    consul 的安装

    1. 通过docker
      运行如下命令:

      docker run -d -p 8500:8500 consul consul agent -data-dir=/consul/data -config-dir=/consul/config -dev -client=0.0.0.0 -bind=0.0.0.0

    2. 通过其它方式
      安装方式参考https://www.consul.io/intro/getting-started/install.html
      安装完后运行:
      consul agent --data-dir . -server -ui -bootstrap -bind=127.0.0.1

    无论哪种方式,运行完之后在浏览器中打开 http://127.0.0.1:8500/ui, 可以看到如下内容

    服务注册

    已之前介绍的gRPC代码为基础,我们加入服务注册部分(注:本人环境为python3, 需要python2的,自己进行修改)

     import time
     import grpc
     import consul
     import json
     from concurrent import futures
     
     import test_pb2_grpc
     import test_pb2
     
     
     def test(request, context):
         # 实际调用到的函数
         json_response = test_pb2.JSONResponse()
         json_response.rst_string = json.dumps({"ret":"Hi gRPC"})# 构造出proto文件中定义的返回值格式
         return json_response
     
     class OrderHandler(test_pb2_grpc.OrderHandlerServicer):
         def create_order(self, request, context):
             return test(request, context)
     
     def register(server_name, ip, port):
         c = consul.Consul() # 连接consul 服务器,默认是127.0.0.1,可用host参数指定host
         print(f"开始注册服务{server_name}")
         check = consul.Check.tcp(ip, port, "10s") # 健康检查的ip,端口,检查时间
         c.agent.service.register(server_name, f"{server_name}-{ip}-{port}",
                 address=ip, port=port, check=check) # 注册服务部分
         print(f"注册服务{server_name}成功")
     
     def unregister(server_name, ip, port):
         c = consul.Consul()
         print(f"开始退出服务{server_name}")
         c.agent.service.deregister(f'{server_name}-{ip}-{port}')
     
     def serve():
         server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
         test_pb2_grpc.add_OrderHandlerServicer_to_server(OrderHandler(), server)
         server.add_insecure_port('[::]:{}'.format(12006))
         register("order_server", "0.0.0.0", 12006)
         server.start()
         try:
             while True:
                 time.sleep(186400)
         except KeyboardInterrupt:
             unregister("order_server", "0.0.0.0", 12006)
             server.stop(0)
     
     serve()
    

    要运行此服务需要先安装 python-consul pip install python-consul
    运行此服务,在浏览器中会出现我们刚刚注册的服务,和可用节点,健康信息,如图

    当使用ctrl+c 退出服务时,会取消注册。也就是会从consul列表里消失。
    注意: 当健康检测失败时,并不意味着服务有问题,仅表示consul服务器和对应的rpc服务的端口连不上而已

    rpc客户端连接

    客户端需要向consul服务器请求,得到可用的rpc服务的ip,端口,在进行连接,我们还是基于之前的客户端代码进行修改,完整代码如下:

     import grpc
     from dns import resolver
     from dns.exception import DNSException
     
     import test_pb2_grpc
     import test_pb2
     
     # 连接consul服务,作为dns服务器
     consul_resolver = resolver.Resolver()
     consul_resolver.port = 8600
     consul_resolver.nameservers=["127.0.0.1"]
     
     
     def get_ip_port(server_name):
        '''查询出可用的一个ip,和端口'''
         try:
             dnsanswer = consul_resolver.query(f'{server_name}.service.consul', "A")
             dnsanswer_srv = consul_resolver.query(f"{server_name}.service.consul", "SRV")
         except DNSException:
             return None, None
         return dnsanswer[0].address, dnsanswer_srv[0].port
     
     ip, port = get_ip_port("order_server")
     
     channel = grpc.insecure_channel(f"{ip}:{port}")
     stub = test_pb2_grpc.OrderHandlerStub(channel)
     # 要完成请求需要先构造出proto文件中定义的请求格式
     ret = stub.create_order(test_pb2.OrderRequest(phone="12990", price="50"))
     
     print(ret.rst_string)
    

    总结

    至此,我们已经搭建好了一套简单服务注册-发现系统。当然consul的功能远不止这一点,它还支持分布式,多节点部署。需要了解更多,可以去官网做进一步了解。

    踩过的坑

    • 在示例中,我们只捕获了ctrl+c 信号,在实际中,服务会因为一些奇怪的问题宕掉,而此时,该节点信息会一直在consul里面,除非手动注销,所以需要捕获所有异常信号。
    • 单节点的时候,consul服务挂掉会影响所有相关的项目,所以最好还是有从节点。
  • 相关阅读:
    线程与进程
    进程间通信之信号量与信号灯
    进程间通信之消息队列
    进程间通信之共享内存
    进程间通信之信号
    进程间通信之管道
    软件需求分析
    团队介绍
    EF Core(1.DBFirst)
    7.基本方式调用Api(http api)
  • 原文地址:https://www.cnblogs.com/yuzhenjie/p/9398569.html
Copyright © 2020-2023  润新知