前言
鉴于近期的手头的事有点忙,所以停更了,为不负所望,还是需要把之前一些东西继续写完,就算是给自己一个交代。
接上一篇中,我们主要讲到关于服务的批量注册,这一篇参考来定义我们的类似fastapi的事件及中间件的添加:
事件添加示例:
方式1:
@app.on_event('startup')
def startup():
print("startup=========1》")
sync_redis_client.init_app(app)
@app.on_event('shutdown')
def shutdown():
print("shutdown=========》")
方式2:
# 添加事件处理
def ceshiguanbi():
print("关闭了!")
app.add_event_handler(event_type='shutdown', func=ceshiguanbi)
# 添加服务处理
中间件添加示例:
方式1:
@app.add_middleware
class ServiceLogMiddleware2222(BaseMiddleware):
def before_handler(self, request: Any, context: grpc.ServicerContext, method_name):
pass
print("我是自定义的中间件信息---》1")
def after_handler(self, context: grpc.ServicerContext, response):
pass
print("我是自定义的中间件信息---》2")
def error_grpc_exc_handler(self, error):
pass
方式2:
# 添加服务处理
# app.add_servicer(Greeter)
from grpcframe.middlewares.tracer.middleware import Zyxopentracing
app.add_middleware(Zyxopentracing)
from example.hello_srv.middlewares.authinterceptor import AuthMiddleware
# PS: 关于中间件注册,最后注册,先执行,遵循的是洋葱模型,(需要统一基于Bsea等Middleware,原生的会首先执行!)
app.add_middleware(AuthMiddleware)
事件注册篇
事件注册方式
鉴于框架结构之前还有些不是非常好,所以新的有所调整了一下,具体调整如下:
相关的时间注册的示例如代码所示:
from grpcframe.zyxgrpc.base_app import FastGrpcApp
from grpcframe.exts.redis.client import sync_redis_client
from grpcframe.exts.register.consul import ConsulRegister
from grpcframe.zyxgrpc.base_middleware import BaseMiddleware,Any
import grpc
# 注册中心
register = ConsulRegister(host='127.0.0.1',port=8500, server_name='FastGrpcApp',server_id="23423424234")
# 实例化一个带有注册中心的服务对象
app = FastGrpcApp(port=45564,max_workers=20,is_certificate=False,register_center=None,debug=True)
@app.on_event('startup')
def startup():
print("startup=========1》")
sync_redis_client.init_app(app)
@app.on_event('shutdown')
def shutdown():
print("shutdown=========》")
if __name__ == '__main__':
# 单独的进行加载
app.run_serve()
封装思路和步骤
这里主要基础参考fastapi的我设计,很简单不复杂。
第1步:定义存贮注册函数列表
# 定义事件回调列表
self.on_startup = [] if on_startup is None else list(on_startup)
self.on_shutdown = [] if on_shutdown is None else list(on_shutdown)
第2步:定义添加事件函数或装饰器函数
# 通过装饰器添加事件处理
def on_event(self, event_type: str) -> typing.Callable:
def decorator(func: typing.Callable) -> typing.Callable:
self.add_event_handler(event_type, func)
return func
return decorator
# 添加事件信号处理器
def add_event_handler(self, event_type: str, func: typing.Callable) -> None:
# 根据类型来识别添加不懂的函数对象
assert event_type in ("startup", "shutdown")
if event_type == "startup":
self.on_startup.append(func)
else:
self.on_shutdown.append(func)
第3步:定义事件处理函数
# ===============事件处理模块机制===============
def startup(self) -> None:
for handler in self.on_startup:
handler()
def shutdown(self) -> None:
for handler in self.on_shutdown:
handler()
第4步:在合适的位置进行事件处理函数的调用
总结:其实总结起来就是使用立碑存贮一堆的函数,在恰当时机进行函数遍历调用。
中间件篇
中间件定义示例:
from grpcframe.zyxgrpc.base_middleware import BaseMiddlewareGrequestcontext
from typing import Any
from grpc_interceptor.exceptions import GrpcException
import grpc
from grpcframe.exts.grpclocal.request import Request
# 为了的到类型提示
from grpcframe.zyxgrpc.globals import grequestcontext
grequestcontext :Request= grequestcontext
# @app.add_middleware
class AuthMiddleware(BaseMiddlewareGrequestcontext):
def before_handler(self):
pass
# 设置上下文信息处理
print("我输出----没初始化吗?", grequestcontext.parse_request_metadata)
print("我输---没初始化吗?AuthInterceptor")
# 创建类似flask的上下文的对象
def after_handler(self):
pass
def error_grpc_exc_handler(self,e:GrpcException):
pass
def finally_handler(self):
pass
目前中间支持几种基础的继承的方式,和需要来定义继承扩展来实现:
主要的几个模式有:
from grpc_interceptor import ServerInterceptor
from grpc_interceptor.exceptions import GrpcException
import abc
import grpc
from typing import Any, Callable
from grpcframe.zyxgrpc.compose_app import ComposeApp
# 扩展需要的错误处理
class ErrorBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def error_grpc_exc_handler(self, exception: GrpcException):
raise NotImplementedError()
# 扩展需要的错误处理
class FinallyBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def finally_handler(self):
raise NotImplementedError()
class BeforeAfterBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def before_handler(self, request: Any, context: grpc.ServicerContext, method_name):
raise NotImplementedError()
@abc.abstractmethod
def after_handler(self, context: grpc.ServicerContext, response):
raise NotImplementedError()
class BeforeAfterBaseNoParameter(metaclass=abc.ABCMeta):
@abc.abstractmethod
def before_handler(self):
raise NotImplementedError()
@abc.abstractmethod
def after_handler(self):
raise NotImplementedError()
class BaseMiddlewareNoParameter(grpc.ServerInterceptor, BeforeAfterBaseNoParameter):
'''
如果是基于grpc.ServerInterceptor实现的中间件,在进行中间件的循环的时候,不遵循了洋葱模型,到账可能对应的回去不到对应的信息了,
# 它是永远的第一个去执行了!!!!!!!
'''
def intercept_service(self, continuation, handler_call_details):
self.before_handler()
resp = continuation(handler_call_details)
self.after_handler()
return resp
# 需要先继承ServerInterceptor再继承ABCMeta
class BaseComposeAppMiddleware(ServerInterceptor, ErrorBase, BeforeAfterBase, ComposeApp):
def intercept(self, method: Callable, request: Any, context: grpc.ServicerContext, method_name: str, ) -> Any:
# 创建上下文对象
try:
# 回调调用必要的前置的函数处理
self.before_handler(request, context, method_name)
response = method(request, context)
# 调用必要的后置的函数处理
self.after_handler(context, response)
return response
except GrpcException as e:
self.error_grpc_exc_handler(e)
# context.set_code(e.status_code)
# context.set_details(e.details)
raise
finally:
pass
# 有request, context回调处理的基础
class BaseMiddleware(ServerInterceptor, ErrorBase, BeforeAfterBase):
def intercept(self, method: Callable, request: Any, context: grpc.ServicerContext, method_name: str, ) -> Any:
# 创建上下文对象
try:
# 回调调用必要的前置的函数处理
self.before_handler(request, context, method_name)
response = method(request, context)
# 调用必要的后置的函数处理
self.after_handler(context, response)
return response
except GrpcException as e:
self.error_grpc_exc_handler(e)
# context.set_code(e.status_code)
# context.set_details(e.details)
raise
finally:
pass
# 有request, context回调处理的基础
class BaseMiddlewareGrequestcontext(ServerInterceptor, ErrorBase, BeforeAfterBaseNoParameter, FinallyBase):
def intercept(self, method: Callable, request: Any, context: grpc.ServicerContext, method_name: str, ) -> Any:
# 创建上下文对象
try:
# 回调调用必要的前置的函数处理
self.before_handler()
response = method(request, context)
# 调用必要的后置的函数处理
self.after_handler()
return response
except GrpcException as e:
self.error_grpc_exc_handler(e)
# context.set_code(e.status_code)
# context.set_details(e.details)
raise
finally:
self.finally_handler()
中间件注册使用的示例代码如下:
from grpcframe.zyxgrpc.base_app import FastGrpcApp
from grpcframe.exts.redis.client import sync_redis_client
from grpcframe.exts.register.consul import ConsulRegister
from grpcframe.zyxgrpc.base_middleware import BaseMiddleware,Any
import grpc
# 注册中心
register = ConsulRegister(host='127.0.0.1',port=8500, server_name='FastGrpcApp',server_id="23423424234")
# 实例化一个带有注册中心的服务对象
app = FastGrpcApp(port=45564,max_workers=20,is_certificate=False,register_center=None,debug=True)
@app.add_middleware
class ServiceLogMiddleware2222(BaseMiddleware):
def before_handler(self, request: Any, context: grpc.ServicerContext, method_name):
pass
print("我是自定义的中间件信息---》1")
def after_handler(self, context: grpc.ServicerContext, response):
pass
print("我是自定义的中间件信息---》2")
def error_grpc_exc_handler(self, error):
pass
# 添加服务处理
# app.add_servicer(Greeter)
from grpcframe.middlewares.tracer.middleware import Zyxopentracing
app.add_middleware(Zyxopentracing)
from example.hello_srv.middlewares.authinterceptor import AuthMiddleware
# PS: 关于中间件注册,最后注册,先执行,遵循的是洋葱模型,(需要统一基于Bsea等Middleware,原生的会首先执行!)
app.add_middleware(AuthMiddleware)
if __name__ == '__main__':
# 单独的进行加载
app.run_serve()
封装思路和步骤
部分中间件前置说明
部分的中间件引用用到对应的一个全局quest的上下文的代理,需要需要默认的注册一个全局代理中间件。
关于代理模式,在之前一篇有相关介绍,这里新增几个需要代理的对象,
import contextvars
import typing
from dataclasses import dataclass
from grpcframe.utils.singleton import Singleton
from grpcframe.exts.grpclocal.local import LocalProxy
from functools import partial
@dataclass
class GManager(metaclass=Singleton):
# 记录当前请求的请求
current_app_request = contextvars.ContextVar('current_app_request', default=None)
# 记录当前请求的请求
current_app_context = contextvars.ContextVar('current_app_context', default=None)
# 记录当前被激活的追踪链路的span
active_tracer_span = contextvars.ContextVar('current_active_tracer_span', default=None)
# 包含咯当前的请求乡下问,和context上下文的对象
current_app_request_context = contextvars.ContextVar('current_app_request_context', default=None)
_g = GManager()
def set_current_app_request(value):
_g.current_app_request.set(value)
def get_current_app_request(_g=None):
# 代理里面的get方法直接的返回,这样就不需要显示的调用get()
return _g.current_app_request.get()
def set_current_app_request_context(value):
_g.current_app_request_context.set(value)
def get_current_app_request_context(_g=None):
# 代理里面的get方法直接的返回,这样就不需要显示的调用get()
return _g.current_app_request_context.get()
def get_current_app_request_context2():
# 代理里面的get方法直接的返回,这样就不需要显示的调用get()
return _g.current_app_request_context.get()
def set_current_app_context(value):
_g.current_app_context.set(value)
def get_current_app_context(_g=None):
# 代理里面的get方法直接的返回,这样就不需要显示的调用get()
return _g.current_app_context.get()
def set_active_tracer_span(value):
_g.active_tracer_span.set(value)
def get_active_tracer_span(_g=None):
# 代理里面的get方法直接的返回,这样就不需要显示的调用get()
return _g.active_tracer_span.get()
# 定义 一个全局代理g里面的current_app_request 的对象,这样也可以实现类似flask上线文的效果
grequest = LocalProxy(partial(get_current_app_request, _g))
gcontext = LocalProxy(partial(get_current_app_context, _g))
gactive_tracer_span = LocalProxy(partial(get_active_tracer_span, _g))
grequestcontext = LocalProxy(partial(get_current_app_request_context, _g))
其他步骤说明:
第1步:定义基础的相关中间件基类
不同的基类实现不同的需要实现的方式,具体可以看上面的基类模式。
第2步:通过添加函数的方式进行添加
使用装饰器形式的话是对类的装饰,这个地方有需要的话你可以另外自己封装处理一下。
from example.hello_srv.middlewares.authinterceptor import AuthMiddleware
# PS: 关于中间件注册,最后注册,先执行,遵循的是洋葱模型,(需要统一基于Bsea等Middleware,原生的会首先执行!)
app.add_middleware(AuthMiddleware)
上面两点是关于事件和中间件模块的一些使用和思路简单介绍,鉴于时间有限,叙述不够详细。如果有相关疑问的话,并且当前这个封装应该是会有坑的,如果你在使用过程有相关的问题,也愿意和我沟通交流的话,可以随时的联系我。我也是小白,相互学习学习!
关于开源
fastapi脚手架封装
之前第一版fastapi开源后一个脚手架,里面部分内容其实封装不是很合理,但是也的到一些网友门的关注,实在是受宠若惊,
其实第一版发布之后忙于其他事情,就暂时搁置,后来在闲暇之余也重构了一版。
以下是最新版本的一些结构,新版中主要新增了一些批量加载又有和一些基础公共的扩展封装。
后续等手头忙完之后就会开源更新一下。
grpc脚手架封装
该框架,基本主要是想把使用grpc的方式改造成和使用fastapi的模式差不多的。但是功力有限,部分的东西目前还在时间继续深入改造,比如改造为使用route这种方式来实现相关的方式注册。目前主要是还是CBV的方式来实现。
主要实现的功能点有:
- 事件注册
- 服务批量注册
- 中间件添加
- 全局上下文代理实现
- 基于信号库的事件处理
- 引入pydantic做简单的参数校验
- 默认健康检测的实现
项目结构如下:
开源地址
https://gitee.com/xiaozhong1988/fastgrpc
有坑在所难免,希望大佬可以多多指教!
总结
以上仅仅是个人结合自己的实际需求,做学习的实践笔记!如有笔误!欢迎批评指正!感谢各位大佬!
结尾
END
简书:www.jianshu.com/u/d6960089b…
公众号:微信搜【小儿来一壶枸杞酒泡茶】
小钟同学 | 文 【欢迎一起学习交流】| QQ:308711822