服务注册与发现

       consul提供服务注册与健康检查功能,服务使用方可监视对应服务的情况(要启动专用线程),但是没找到对应的使用方式。 

只找到一个建康检查的接口(示例:http://localhost:8500/v1/health/service/test_server?passing),通过定时调用该接口可获取健康的服务列表,与之前的相比较,然后做相应工作。

       etcd注册服务时可设定ttl,然后服务要专门启动一个线程定时刷新该ttl,相当保持心跳,通过这种机制注册服务及实现健康检查功能。服务使用方可调用监视接口,监视对应服务是否有变化。

 

       服务使用方根据获取的服务列表,每台机器启动一个连接池(这个连接池可使用第三方的实现),并管理该机器组及对应连接池(这种方式一般是使用了第三方框架,比如go的http, gin,beego或是python的flask,django等,上层使用方是多线程的且不可控)。客户端做负载均衡,策略可根据实际情况实现,比如轮询的方式。在使用时如果发现连接不可用,可根据错误情况做相应工作,如果是断开连接就要 剔除该机器。服务重连交给服务监视线程来做。读写连接机器时用读写锁做同步,可允许两个不同线程拿同一机器的连接。机器的连接池用普通锁同步,防止两个不同线程同时拿同一个连接。

       如果使用方的并发线程是固定的(这种方式一般整个框架代码都是自己实现,没用第三方的框架),则对每个线程启动对每台机器的一个连接,根据负载均衡策略线程内做负载均衡。这种方式最高效,建立的连接少,拿连接时不用锁。检查线程发现有更新时通知工作线程做连接管理的更新。检查线程与工作线程发消息可用环形队列实现的不加锁的线程安全队列来做。检查线程是生产者,工作线程是消费者,工作线程发现有更新时做更新操作,全程无锁实现。这种模型是最高效的。

 

下面的例子是基于上篇python的grpc示例,使用consul做服务的注册与发现:

1.下载consul

开发形式启动:

./consul agent -dev 

2.服务端server.py(启动)

#coding=utf-8
from concurrent import futures
import time
import grpc
import grpchello_pb2
import grpchello_pb2_grpc
import consul


def register(server_name, ip, port):
    c = consul.Consul() # 连接consul 服务器,默认是127.0.0.1,可用host参数指定host
    print("开始注册服务{}".format(server_name))
    check = consul.Check.tcp(ip, port, "10s") # 健康检查的ip,端口,检查时间
    c.agent.service.register(server_name, "{}-{}-{}".format(server_name, ip, port), address=ip, port=port, check=check) # 注册服务部分
    print("注册服务{}成功".format(server_name))
 
def unregister(server_name, ip, port):
    c = consul.Consul()
    print("开始退出服务{}".format(server_name))
    c.agent.service.deregister('{}-{}-{}'.format(server_name, ip, port))
 

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

#继承接口基类,并实现基类中的方法
class MyServer(grpchello_pb2_grpc.gRPCServicer):
    def SayHello(self, request, context):
        print(request.name)
        message = "hello,this is my test result:" + request.name
        return grpchello_pb2.HelloReply(message = message)
    def Calc(selr, request, context):
        ret = 0
        if request.para3 == "+":
            ret = request.para1 + request.para2
        elif request.para3 == "-":
            ret = request.para1 - request.para2
        elif request.para3 == "*":
            ret = request.para1 * request.para2
        elif request.para3 == "/":
            ret = request.para1 / request.para2
        else:
            ret = 0
        print(str(request.para1) + str(request.para3) + str(request.para2) + "=" + str(ret))
        return grpchello_pb2.CalcReply(ret = ret)


def server():
    #设置服务线程数
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    #注册服务
    grpchello_pb2_grpc.add_gRPCServicer_to_server(MyServer(), server)
    #设置服务ip和port
    server.add_insecure_port('[::]:50051')
    register("test_server", "127.0.0.1", 50051)
    print("sever is opening ,waiting for message...")
    #非阻塞启动
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    server()

3.服务端server2.py(启动)

#coding=utf-8
from concurrent import futures
import time
import grpc
import grpchello_pb2
import grpchello_pb2_grpc
import consul


def register(server_name, ip, port):
    c = consul.Consul() # 连接consul 服务器,默认是127.0.0.1,可用host参数指定host
    print("开始注册服务{}".format(server_name))
    check = consul.Check.tcp(ip, port, "10s") # 健康检查的ip,端口,检查时间
    c.agent.service.register(server_name, "{}-{}-{}".format(server_name, ip, port), address=ip, port=port, check=check) # 注册服务部分
    print("注册服务{}成功".format(server_name))
 
def unregister(server_name, ip, port):
    c = consul.Consul()
    print("开始退出服务{}".format(server_name))
    c.agent.service.deregister('{}-{}-{}'.format(server_name, ip, port))
 

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

#继承接口基类,并实现基类中的方法
class MyServer(grpchello_pb2_grpc.gRPCServicer):
    def SayHello(self, request, context):
        print(request.name)
        message = "hello,this is my test result:" + request.name
        return grpchello_pb2.HelloReply(message = message)
    def Calc(selr, request, context):
        print("req.para1:" + str(request.para1))
        print("req.para2:" + str(request.para2))
        print("req.para3:" + str(request.para3))
        ret = 0
        if request.para3 == "+":
            ret = request.para1 + request.para2
        elif request.para3 == "-":
            ret = request.para1 - request.para2
        elif request.para3 == "*":
            ret = request.para1 * request.para2
        elif request.para3 == "/":
            ret = request.para1 / request.para2
        else:
            ret = 0
        print(str(request.para1) + str(request.para3) + str(request.para2) + "=" + str(ret))
        return grpchello_pb2.CalcReply(ret = ret)


def serve():
    #设置服务线程数
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    #注册服务
    grpchello_pb2_grpc.add_gRPCServicer_to_server(MyServer(), server)
    #设置服务ip和port
    server.add_insecure_port('[::]:50052')
    register("test_server", "127.0.0.1", 50052)
    print("sever is opening ,waiting for message...")
    #非阻塞启动
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

3.客户端client.py(启动)

#coding:utf-8
from __future__ import print_function

import grpc
import time
import grpchello_pb2
import grpchello_pb2_grpc
from server_group import *

#通道池的设置
#监视服务:发现有变化时,要看和之前的比较,如果是增加,就新建一组该服务的通道,如果是减少,就删除该服务的对应组的通道

def run():


    #这样创建的通道是线程安全的通道
    channel = global_server_group.get_channel()
    print("channel:", channel)
    stub = grpchello_pb2_grpc.gRPCStub(channel)
    print("stub:", stub)

    response = stub.Calc(grpchello_pb2.CalcRequest(para1 = 2, para2 = 1, para3 = "+"))
    print(response)
 
    count = 0
    while True:
        count += 1
        channel = global_server_group.get_channel()
        try:
            print("channel:", channel)
            stub = grpchello_pb2_grpc.gRPCStub(channel)
            stub.Calc(grpchello_pb2.CalcRequest(para1 = count, para2 = 1, para3 = "+"))
            time.sleep(1)
        except Exception as err:
            global_server_group.del_dead_channel(channel)
            print(err)
            continue

if __name__ == '__main__':
    start_server_check_thread()
    time.sleep(3)
    run()

server_group.py(健康服务的定时获取,更新等服务管理)

#coding:utf-8
import consul
import requests
import json
import gevent
import time
import grpc
from gevent import monkey
#import Queue

monkey.patch_all()

class server_group(object):
    def __init__(self, address):
        self.channel = []
        self.address = address#参数是一个集合类型
        self.address_channel = {}
        self.channel_address = {}
        for add in self.address:
            #这个是线程安全通道
            _channel = grpc.insecure_channel(add)
            self.channel.append(_channel)
            self.address_channel[add] = _channel
            self.channel_address[_channel] = add
        self.index = 0
        self.len = len(self.channel)
        self.RWLock = RWLock()

    def get_channel(self):
        
        self.RWLock.read_acquire()
        
        _channel = self.channel[self.index]
        self.index += 1
        if self.index >= self.len:
            self.index = 0
       
        print("len:" + str(self.len) + "\tindex:" + str(self.index))
        self.RWLock.read_release()
       
        return _channel
    
    def del_dead_channel(self, _channel):
        self.RWLock.write_acquire()
        add = self.channel_address.get(_channel)
        if add and _channel:
            del self.address_channel[add]
            del self.channel_address[_channel]
            self.channel.remove(_channel)
            self.address.remove(add)
        
        self.index = 0
        self.len = len(self.channel)

        self.RWLock.write_release()
        return True
    #要增加的服务,要删除的服务,交集,老的减交集为要删除的,新的减交集为要增加的
    #加读写锁同步,读时允计多个线程同时拿同一个index
    def update_server_group(self, address):
        if self.address == address:
            print("没有更新")
            return False
        
        for add in self.address:
            print("老的add:" + add)
        for add in address:
            print("新的add:" + add)

        print("进入更新")
        self.RWLock.write_acquire()
        
        #交集
        com = (self.address).intersection(address)
        del_address = self.address - com
        add_address = address - com
        self.address = com.union(add_address)
        
        for add in del_address:
            _channel = self.address_channel.get(add)
            if _channel:
                del self.address_channel[add]
                del self.channel_address[_channel]
                self.channel.remove(_channel)
        
        for add in add_address:
            _channel = grpc.insecure_channel(add)
            self.channel.append(_channel)
            self.address_channel[add] = _channel
            self.channel_address[_channel] = add
        
        self.index = 0
        self.len = len(self.channel)

        self.RWLock.write_release()
        
        return True


import threading
class RWLock(object):
    def __init__(self):
        self.rlock = threading.Lock()
        self.wlock = threading.Lock()
        self.reader = 0
    #写加锁
    def write_acquire(self):
        self.wlock.acquire()

    def write_release(self):
        self.wlock.release()

    #读加锁
    def read_acquire(self):
        self.rlock.acquire()
        self.reader += 1
        if self.reader == 1:
            self.wlock.acquire()
            self.rlock.release()
    def read_release(self):
        self.rlock.acquire()
        self.reader -= 1
        if self.reader == 0:
            self.wlock.release()
            self.rlock.release()
def get_health_server():
    address = set()
    #通过如下方式可以获取服务端健康的服务
    url = "http://localhost:8500/v1/health/service/test_server?passing"
    resp = requests.get(url)
    #print(resp)
    #print(resp.text)
    my_dict = json.loads(resp.text)
    for value in my_dict:
        service = value["Service"]
        ip_port = str(service["Address"]) + ":" + str(service["Port"])
        #print(ip_port)
        address.add(ip_port)
    return address


def server_check():
    while True:
        address = get_health_server()
        for add in address:
            print(add)
        global_server_group.update_server_group(address)
        time.sleep(1)


global_server_group = server_group(set())

def start_server_check_gevent():
    gevent.spawn(server_check)
def start_server_check_thread():
    t = threading.Thread(target = server_check)
    t.start()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值