精华内容
下载资源
问答
  • 我们分三节来学习下Django的中间件,首先学习下中间件本质以及处理流程,然后用添加反爬功能为例做一个简单的自定义中间件。最后我们深入一点,学习下系统自带防跨站攻击的csrf中间件,并针对该中间件的源码看看...

    中间件,是对原有MTV模型的一种补充,可以将其理解为添加额外功能的插件。我们分三节来学习下Django的中间件,首先学习下中间件的本质以及处理流程,然后用添加反爬功能为例做一个简单的自定义中间件。最后我们深入一点,学习下系统自带防跨站攻击的csrf中间件,并针对该中间件的源码看看如何让自己的请求被csrf信任。

    我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

    中间件

    中间件(Middleware),是Django的一个插件框架,用来给请求和响应,甚至是异常,添加额外的功能。说到额外功能,大家可能最先想到的就是Python的装饰器(decorator),没错,Django中间件的本质其实就是一个类装饰器

    配置和激活中间件

    Django拥有很多自带的中间件,创建完项目,打开settings.py,就能看到如下配置

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

    这些就是Django为我们准备的中间件。如果还要添加新的中间件,往这个list中添加即可。但是要注意,和INSTALLED_APPS这种配置不同,中间件存在一个执行顺序的问题,所以放在list头和尾是有很大区别的。下面的处理流程中我们再详细说。

    创建一个自己的中间件也非常容易,只需要创建一个类,继承系统的MiddlewareMixin,然后再将创建的类添加到上面的list中即可。

    例如,我在项目的根目录创建了一个文件夹middleware,新建文件testmiddle.py,里面新建类如下

    from django.utils.deprecation import MiddlewareMixin
    
    
    class HelloMiddle(MiddlewareMixin):
        pass
    

    这样就相当于新建了一个自己的中间件HelloMiddle,然后将其完整路径添加到配置中

    MIDDLEWARE = [
        'middleware.testmiddle.HelloMiddle',
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

    我们不妨点进去看看这个MiddlewareMixin到底是个啥

    class MiddlewareMixin:
        def __init__(self, get_response=None):
            self.get_response = get_response
            super().__init__()
    
        def __call__(self, request):
            response = None
            if hasattr(self, 'process_request'):
                response = self.process_request(request)
            response = response or self.get_response(request)
            if hasattr(self, 'process_response'):
                response = self.process_response(request, response)
            return response
    

    先不管具体代码,一看这个结构,__init__加上__call__,这就是一个典型的类装饰器。所以才说Django的本质就是一个类装饰器。我们通过重写类装饰器的特定方法,达到在请求不同阶段添加插件的目的

    想对python装饰器了解更多可以参考我的另一篇博客《一篇文章汇总Python装饰器全知识图谱(使用场景,基本用法,参数传递,闭包操作,类装饰器和AOP)》

    AOP和中间件处理流程

    说到中间件和装饰器,就不得不提一下AOP这个东西,也就是Aspect Oriented Programming,面向切面编程。

    Python中AOP的本质也是装饰器,但是根据被装饰函数位置的不同分为不同的切点,同时根据装饰器功能的不一样又分为不同的切面。

    所以说来说去,中间件,AOP和装饰器,在Django这里其实说的都是同一回事。

    几个切点

    中间件这个装饰器,只能在如下5个切点添加插件功能,换句话说,在自己定义的中间件类中,只能重写如下5个方法,来达到在MTV不同阶段添加插件的目的。

    而这5个切入点,发生在请求阶段的为如下两个,按照配置中所有中间件从上至下执行

    • process_request(self, request)
    • process_view(self, request, func_view, func_args, func_kwargs)

    发生在响应阶段的为如下三个,按照配置中所有中间件从下至上执行

    • process_exception(self, request, exception)
    • process_template_response(self, request, response)
    • process_response(self, request, response)

    所以配置文件里面的每个中间件都可能有这5个方法,这么多中间件,每个中间件又有这么多方法,具体的处理流程是啥样的呢?

    如果熟悉linux的防火墙iptables的朋友可以将这一个个的中间件类比为iptables中的表,而每个方法可以看作iptables中的链。下面我们来仔细看看。

    处理流程

    如下图所示
    1-AOP.jpg
    红色的四个切点分别对应着①②⑦⑧四个阶段,而图中没有出现的process_exception则是在view函数执行阶段触发的。

    • process_request
      用户发起的请求,在还没路由的时候,会按照配置中所有中间件从上至下执行process_request方法。如果有任意一个中间件的process_request返回HttpResponse对象则请求被中断,之后从目前的中间件开始从下至上执行process_response方法。如果所有中间件的process_request都返回None,则进入到下一步urls.py中判断路由

    • process_view
      在确定了view函数但还没执行的时候,和上面类似,从上至下执行process_view方法,如果有任意一个中间件的process_view返回HttpResponse对象则请求被中断,之后从目前的中间件开始从下至上执行process_response方法。如果所有中间件的process_view都返回None,则进入下一步views.py中执行具体view函数

    • process_template_response
      view函数执行阶段,如果一切正常,且返回的对象有render方法时,则从下至上执行process_template_response方法。注意该方法必须返回一个HttpResponse对象。

    • process_exception
      view函数执行阶段,如果出现异常,则从下至上执行process_exception方法。如果某一个中间件返回HttpResponse对象,则跳过上方剩余的中间件,开始从最下面执行process_response

    • process_response
      当前面所有的步骤执行完毕后,开始从下至上执行process_response方法,直到最上层的中间件执行完毕后,返回给用户。注意该方法必须返回一个HttpResponse对象。

    验证

    下面我们就来创建几个中间件来验证下上面的这个流程。

    MIDDLEWARE = [
        'middleware.testmiddle.Test1',
        'middleware.testmiddle.Test2',
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'middleware.testmiddle.Test3',
        'middleware.testmiddle.Test4',
    ]
    

    创建了Test1 - Test4一共四个中间件,其中两个在头两个在尾。四个中间件定义如下

    class Test1(MiddlewareMixin):
        def process_request(self, request):
            print('process request test1')
    
        def process_view(self, request, func_view, func_args, func_kwargs):
            print('process view test1')
    
        def process_exception(self, request, exception):
            print('process exception test1')
            # return HttpResponse('hello')
    
    
        def process_template_response(self, request, response):
            print('process template response test1')
            return response
    
        def process_response(self, request, response):
            print('process response test1')
            return response
    
    class Test2(MiddlewareMixin):
        def process_request(self, request):
            print('process request test2')
    
        def process_view(self, request, func_view, func_args, func_kwargs):
            print('process view test2')
    
        def process_exception(self, request, exception):
            print('process exception test2')
    
        def process_template_response(self, request, response):
            print('process template response test2')
            return response
    
        def process_response(self, request, response):
            print('process response test2')
            return response
    
    
    class Test3(MiddlewareMixin):
        def process_request(self, request):
            print('process request test3')
    
        def process_view(self, request, func_view, func_args, func_kwargs):
            print('process view test3')
    
        def process_exception(self, request, exception):
            print('process exception test3')
            return HttpResponse('Oh error')
    
        def process_template_response(self, request, response):
            print('process template response test3')
            return response
    
        def process_response(self, request, response):
            print('process response test3')
            return response
    
    
    class Test4(MiddlewareMixin):
        def process_request(self, request):
            print('process request test4')
    
        def process_view(self, request, func_view, func_args, func_kwargs):
            print('process view test4')
    
        def process_exception(self, request, exception):
            print('process exception test4')
    
        def process_template_response(self, request, response):
            print('process template response test4')
            return response
    
        def process_response(self, request, response):
            print('process response test4')
            return response
    

    需要注意的是,一旦发生异常,会在Test3那里返回HttpResponse('oh error')

    之后创建路由和view函数如下

    path('test_middleware/',views.test_middleware, name='test_middleware'),
    
    class test():
        def render(self):
            a = 1/0
            return HttpResponse('Life is awesome')
    
    def test_middleware(request):
        return test()
    

    如前所述,为了触发process_template_response方法,这里返回一个带render方法的对象。这里在最终返回前有一个除以0的错误。

    打印结果如下

    process request test1
    process request test2
    process request test3
    process request test4
    process view test1
    process view test2
    process view test3
    process view test4
    process template response test4
    process template response test3
    process template response test2
    process template response test1
    process exception test4
    process exception test3
    process response test4
    process response test3
    process response test2
    process response test1
    

    可见在Test3将异常处理后,直接进入了Test4的process_response

    几个注意事项

    • 假如view函数中异常抛出在返回一个带render方法的对象之前,那么也是不会触发process_template_response的。例如上面将a = 1/0移到return test()之前。

    • 一旦异常抛出,则该view函数的原本返回值作废,如果所有的process_exceptionprocess_response都没有返回HttpResponse对象,则用户看到5xx错误码。所以通常统一在异常处理中跳转到更友好的页面以防止没有被捕捉的异常被用户看见。

    • 为了触发process_template_response,这里构造了一个带render方法的类,但是实际中一般直接在view函数中返回render对象,所以其实很难有机会触发process_template_response方法

    实际应用

    理论知识还是挺枯燥的,下面就来几个实际应用来感受下。在配置中将上面的四个中间件只留下Test1来操作。

    统计信息

    在用户请求处理前可以对所有请求的信息进行统计和分析,例如用户的IP,浏览器类型,操作系统等等。

    如果要统计用户的IP

    def process_request(self, request):
            print(request.META.get('REMOTE_ADDR'))
    

    当然这里是打印,实际使用可以结合logging或者是数据库来进行存储。

    权重和黑白名单

    既然可以知道用户的信息,就可以根据这些信息对请求做一些预处理。

    例如创建一个抢票网站,普通用户有50%概率抢到,而对于某些IP段可以限制其概率为20%甚至是0。这种预处理功能虽说可以加到view函数中来实现,但是出于低耦合的目的,建议是单独放在中间件中。

    创建路由和view函数如下

    path('get_ticket/', views.get_ticket, name='get_ticket'),
    
    def get_ticket(request):
        if random.randint(0, 100) > 50:
            return HttpResponse('Congratulations!!')
        else:
            return HttpResponse('Sorry, better luck next time!!')
    

    这个时候用户访问抢到票的概率约为50%。

    修改中间件Test1的process_request如下

    class Test1(MiddlewareMixin):
        def process_request(self, request):
            ip = request.META.get('REMOTE_ADDR')
            if ip.startswith('127.0.'):
                if random.randint(0,100)>90:
                    return HttpResponse('Congratulations!!')
                else:
                    return HttpResponse('Sorry!!')
    

    这里为了举例子,将127.0.开头的概率降为了约10%,实际中可以根据用户的cookie等个人信息来做判定。如果要使得页面更为友好,可以在这里返回一个正在排队之类的页面让用户等待。

    总结

    这一节我们学习了中间件的本质是一个装饰器,并验证了五个切点的执行顺序,最后用两个简单的例子感受了下自定义中间件。下一节我们就针对反爬虫的频率控制来制作一个稍微复杂点的中间件。

    展开全文
  • PHP实现中间件核心技术:匿名函数和array_...通过匿名函数实例讲解中件间实更原理,对多个中间件的组织,使数据的流动按我们预定的方式进行,中间件本质是一个洋葱模型。请求是从外到里返回是从里到外一层一层返回。
  • 消息中间件本质上就是一种很简单的数据结构——队列,但是一条队列肯定是当不成中间件的,你必须要考虑性能、容灾、可靠性等等因素。这也给我的写作提供了一些思路,我将从队列开始,给你演示一条队列是如何进化成一...
  • Django中间件

    2019-10-07 00:13:56
    中间件是一个钩子框架,它们可以介入Django 的请求和响应处理过程。 它是一个轻量级、底层的“插件”系统,用于在全局修改Django 的输入或输出。 每个中间件组件负责完成某个特定的功能。 创建一个新django项目时...

    概念

    中间件是一个钩子框架,它们可以介入Django 的请求和响应处理过程。 它是一个轻量级、底层的“插件”系统,用于在全局修改Django 的输入或输出。

    每个中间件组件负责完成某个特定的功能。

    创建一个新django项目时,默认自带的中间件有:(在settings.py内)

     

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]

    • SecurityMiddleware:为请求/相应提供了几种安全改进
    • SessionMiddleware:开启会话支持
    • CommonMiddleware:基于APPEND_SLASHPREPEND_WWW的设置来重写URL,如果APPEND_SLASH设为True,并且初始URL 没有以斜线结尾以及在URLconf 中没找到对应定义,这时形成一个斜线结尾的新URL;如果PREPEND_WWW设为True,前面缺少 www.的url将会被重定向到相同但是以一个www.开头的url
    • CsrfViewMiddleware:添加跨站点请求伪造的保护,通过向POST表单添加一个隐藏的表单字段,并检查请求中是否有正确的值
    • AuthenticationMiddleware:向每个接收到的user对象添加HttpRequest属性,表示当前登录的用户
    • MessageMiddleware:开启基于Cookie和会话的消息支持
    • XFrameOptionsMiddleware:对点击劫持的保护

    中间件执行流程

    从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:

    请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,如果process_request方法返回的值是None,就依次执行;如果返回的值是HttpResponse对象,则不再执行后面的process_request方法,而是执行当前对象中间件的process_response方法,并将HttpResponse对象返回给浏览器。

    也就是说,如果MIDDLEWARE注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4, 5, 6中间件的process_request和process_response方法都不执行,而是顺序执行3, 2, 1中间件的process_response方法。

     
    image.png

    中间件的五种方法

    中间件中可以定义5个方法,分别是:

    • process_request(self,request) 发送请求
    • process_view(self, request, callback, callback_args, callback_kwargs) 执行完 request 预处理函数并确定待执行的 view 之后,但在 view 函数实际执行之前。
    • process_template_response(self,request,response) response参数应该是一个由view或者中间件返回的TemplateResponse对像(或等价的对象)。如果响应的实例有render()方法,process_template_response()会在view刚好执行完毕之后被调用。这个方法必须返回一个实现了render方法的响应对象。
    • process_exception(self, request, exception) 收集错误信息,当一个view引发异常时,Django会调用process_exception()来处理。返回一个None或一个HttpResponse对象。如果返回HttpResponse对象,会将响应交给处理响应的中间件处理。由于处理响应时是从下到上的,此层以上的process_exception()是不会被调用的。
    • process_response(self, request, response) 必须返回 HttpResponse 对象。这个 response 对象可以是传入函数的那一个原始对象(通常已被修改),也可以是全新生成的。

    执行顺序也是按照以上顺序执行的。

     

    process_request

    执行时间:
    在视图函数之前,在路由匹配之前
    
    参数:
    request:请求对象,与视图中用到的request参数是同一个对象
    
    返回值:
    None:按照正常的流程走
    HttpResponse:接着倒序执行当前中间件的以及之前执行过的中间件的process_response方法,不再执行其它的所有方法
    
    执行顺序:
    按照MIDDLEWARE中的注册的顺序执行,也就是此列表的索引值
    

    process_response

    执行时间:
    最后执行
    
    参数:
    request:请求对象,与视图中用到的request参数是同一个对象
    response:响应对象,与视图中返回的response是同一个对象
    
    返回值:
    response:必须返回此对象,按照正常的流程走
    
    执行顺序:
    按照注册的顺序倒序执行
    

    process_view

    执行时间:
    在process_request方法及路由匹配之后,视图之前
    
    参数:
    request:请求对象,与视图中用到的request参数是同一个对象
    view_func:将要执行的视图函数(它是实际的函数对象,而不是函数的名称作为字符串)
    view_args:url路径中将传递给视图的位置参数的元组
    view_kwargs:url路径中将传递给视图的关键值参数的字典
    
    返回值:
    None:按照正常的流程走
    HttpResponse:它之后的中间件的process_view,及视图不执行,执行所有中间件的process_response方法
    
    执行顺序:
    按照注册的顺序执行
    

    process_exception

    此方法只在视图中触发异常时才被执行.

    执行时间:
    视图之后,process_response之前
    
    参数:
    request:请求对象,与视图中用到的request参数是同一个对象
    exception:视图函数异常产生的Exception对象
    
    返回值:
    None:按照正常的流程走
    HttpResponse对象:不再执行后面的process_exception方法
    
    执行顺序:
    按照注册的顺序倒序执行
    

    process_template_response

    此方法必须在视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)时,才被执行.

    render()方法用于返回一个HTML页面.

    执行时间:
    视图之后,process_exception之前
    
    参数:
    request:请求对象,与视图中用到的request参数是同一个对象
    response:是TemplateResponse对象(由视图函数或者中间件产生)
    
    返回值:
    response:必须返回此对象,按照正常的流程走
    
    执行顺序:
    按照注册的顺序倒序执行
    

    自定义中间件

    from django.utils.deprecation import MiddlewareMixin
    # from django.http import HttpResponse
    from django.shortcuts import HttpResponse, redirect # 方式一: class MyMiddleware(MiddlewareMixin): def process_request(self, request): next_url = request.path_info if not request.path_info.startswith("/login/"): # 做登录验证 login_flag = request.session.get("login", "") if not login_flag: return redirect("/login/?next={}".format(next_url)) def process_view(self, request, view_func, view_args, view_kwargs): pass def process_exception(self, request, exception): if isinstance(exception, ValueError): return HttpResponse("404") def process_response(self, request, response): return response # 方式二: class SimpleMiddleware(object): def __init__(self, get_response): self.get_response = get_response # 一次性配置和初始化。 def __call__(self, request): # Code to be executed for each request before # the view (and later middleware) are called. # 这里写的代码会在视图被调用前执行来处理请求 response = self.get_response(request) # 这里写的代码会在视图调用后执行来处理响应 # Code to be executed for each request/response after # the view is called. return response def process_view(self, request, view_func, view_args, view_kwargs): pass def process_exception(self, request, exception): pass def process_template_response(self, request, response): pass 

    中间件注册

    # 在settings.py里的下面列表中添加自定义的中间件来激活该中间件
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'mymiddlewares.middlewares.MyMiddleware', 'mymiddlewares.middlewares.SimpleMiddleware',
    ]

    Django架构总览

    流程图:

     




    转载于:https://www.cnblogs.com/HZLS/p/11367397.html

    展开全文
  • 1. 什么是中间件 我国企业从20世纪80年代开始就逐渐进行信息化建设,由于方法和体系的不成熟,以及企业业务和市场需求的不断变化,一个企业可能同时运行着多个不同的业务系统,这些系统可能基于不同的操作系统、不同...

    该笔记大部分搬运B站学相伴的RabbitMQ,顺便把图文合并记录,便于回顾,仅用于学习!
    视频地址:https://www.bilibili.com/video/BV1dX4y1V73G 作者真的非常好,别白嫖,记得三连
    如有侵权,请联系删除!

    1. 什么是中间件

    • 我国企业从20世纪80年代开始就逐渐进行信息化建设,由于方法和体系的不成熟,以及企业业务和市场需求的不断变化,一个企业可能同时运行着多个不同的业务系统,这些系统可能基于不同的操作系统、不同的数据库、异构的网络环境。现在的问题是,如何把这些信息系统结合成一个有机地协同工作的整体,真正实现企业跨平台、分布式应用。中间件便是解决之道,它用自己的复杂换取了企业应用的简单。

    • 中间件(Middleware)是处于操作系统和应用程序之间的软件,也有人认为它应该属于操作系统中的一部分。人们在使用中间件时,往往是一组中间件集成在一起,构成一个平台(包括开发平台和运行平台),但在这组中间件中必须要有一个通信中间件,即中间件=平台+通信,这个定义也限定了只有用于分布式系统中才能称为中间件,同时还可以把它与支撑软件和使用软件区分开来。

    举例:

    1. RMI(Remote Method Invocations, 远程调用)
    2. Load Balancing(负载均衡,将访问负荷分散到各个服务器中)
    3. Transparent Fail-over(透明的故障切换)
    4. Clustering(集群,用多个小的服务器代替大型机)
    5. Back-end-Integration(后端集成,用现有的、新开发的系统如何去集成遗留的系统)
    6. Transaction事务(全局/局部)全局事务(分布式事务)局部事务(在同一数据库联接内的事务)
    7. Dynamic Redeployment(动态重新部署,在不停止原系统的情况下,部署新的系统)
    8. System Management(系统管理)
    9. Threading(多线程处理)
    10. Message-oriented Middleware面向消息的中间件(异步的调用编程)
    11. Component Life Cycle(组件的生命周期管理)
    12. Resource pooling(资源池)
    13. Security(安全)
    14. Caching(缓存)

    2. 为什么需要使用消息中间件

    具体地说,中间件屏蔽了底层操作系统的复杂性,使程序开发人员面对一个简单而统一的开发环境,减少程序设计的复杂性,将注意力集中在自己的业务上,不必再为程序在不同系统软件上的移植而重复工作,从而大大减少了技术上的负担,中间件带给应用系统的,不只是开发的简便、开发周期的缩短,也减少了系统的维护、运行和管理的工作量,还减少了计算机总体费用的投入。

    为什么要使用MQ消息中间件?它解决了什么问题?
    https://www.jianshu.com/p/2820561158c4

    3. 中间件特点

    为解决分布异构问题,人们提出了中间件(middleware)的概念。中间件是位于平台(硬件和操作系统)和应用之间的通用服务,如下图所示,这些服务具有标准的程序接口和协议。针对不同的操作系统和硬件平台,它们可以有符合接口和协议规范的多种实现。

    在这里插入图片描述
    也许很难给中间件一个严格的定义,但中间件应具有如下的一些特点:
    (1)满足大量应用的需要
    (2)运行于多种硬件和OS平台
    (3)支持分布计算,提供跨网络、硬件和OS平台的透明性的应用或服务的交互
    (4)支持标准的协议
    (5)支持标准的接口

    由于标准接口对于可移植性和标准协议对于互操作性的重要性,中间件已成为许多标准化工作的主要部分。对于应用软件开发,中间件远比操作系统和网络服务更为重要,中间件提供的程序接口定义了一个相对稳定的高层应用环境,不管底层的计算机硬件和系统软件怎样更新换代,只要将中间件升级更新,并保持中间件对外的接口定义不变,应用软件几乎不需任何修改,从而保护了企业在应用软件开发和维护中的重大投资。

    简单说:中间件有个很大的特点,是脱离于具体设计目标,而具备提供普遍独立功能需求的模块。这使得中间件一定是可替换的。如果一个系统设计中,中间件时不可替代的,不是架构、框架设计有问题,那么就是这个中间件,在别处可能是个中间件,在这个系统内是引擎。

    4. 在项目中什么时候使用中间件技术

    在项目的架构和重构中,使用任何技术和架构的改变我们都需要谨慎斟酌和思考,因为任何技术的融入和变化都可能人员,技术,和成本的增加,中间件的技术一般现在一些互联网公司或者项目中使用比较多,如果你仅仅还只是一个初创公司建议还是使用单体架构,最多加个缓存中间件即可,不要盲目追求新或者所谓的高性能,而追求的背后一定是业务的驱动和项目的驱动,因为一旦追求就意味着你的学习成本,公司的人员结构以及服务器成本,维护和运维的成本都会增加,所以需要谨慎选择和考虑。

    但是作为一个开放人员,一定要有学习中间件技术的能力和思维,否则很容易当项目发展到一个阶段在去掌握估计或者在面试中提及,就会给自己带来不小的困扰,在当今这个时代这些技术也并不是什么新鲜的东西,如果去掌握和挖掘最关键的还是自己花时间和花精力去探讨和研究。

    5. 中间件技术及架构的概述

    知识图谱
    在这里插入图片描述

    6. 什么是消息中间件

    6.1 单体架构

    在实际的项目中,大部分的企业项目开发中,在早起都采用的是单体的架构模式
    在这里插入图片描述
    在企业开发当中,大部分的初期架构都采用的是单体架构的模式进行架构,而这种架构的典型的特点:就是把所有的业务和模块,源代码,静态资源文件等都放在一个工程中,如果其中的一个模块升级或者迭代发生一个很小的变动都会重新编译和重新部署项目。这种这狗存在的问题是:

    1. 耦合度太高
    2. 不易维护
    3. 服务器的成本高
    4. 以及升级架构的复杂度也会增大

    这样就有后续的分布式架构系统。 如下:

    6.2 分布式架构

    在这里插入图片描述
    何谓分布式系统:
    通俗一点:就是一个请求由服务器端的多个服务(服务或者系统)协同处理完成
    和单体架构不同的是,单体架构是一个请求发起 jvm调度线程(确切的是 tomcat线程池)分配线程 Thread来处理请求直到释放,而分布式系统是:一个请求时由多个系统共同来协同完成,jvm和环境都可能是独立。如果生活中的比喻的话,单体架构就像建设一个小房子很快就能够搞定,如果你要建设一个鸟巢或者大型的建筑,你就必须是各个环节的协同和分布,这样目的也是项目发展到后期的时候要去部署和思考的问题。我们也不难看出来:分布式架构系统存在的特点和问题如下:

    存在问题:

    1. 学习成本高,技术栈过多
    2. 运维成本和服务器成本增高
    3. 人员的成本也会增高
    4. 项目的负载度也会上升
    5. 面临的错误和容错性也会成倍增加
    6. 占用的服务器端口和通讯的选择的成本高
    7. 安全性的考虑和因素逼迫可能选择 RMI/MQ相关的服务器端通讯

    好处:

    1. 服务系统的独立,占用的服务器资源减少和占用的硬件成本减少,确切的说是:可以合理的分配服务资源,不造成服务器资源的浪费
    2. 系统的独立维护和部署,耦合度降低,可插拔性
    3. 系统的架构和技术栈的选择可以变的灵活(而不是单纯地选择 java)
    4. 弹性的部署,不会造成平台因部署造成的瘫痪和停服的状态

    6.3 基于消息中间件的分布式系统的架构

    没有加一层解决不了的事情!
    在这里插入图片描述

    从上图中可以看出来,消息中间件的是

    1. 利用可靠的消息传递机制进行系统和系统直接的通讯
    2. 通过提供消息传递消息的派对机制,它可以在分布式系统环境下扩展进程间的通讯

    7. 消息中间件应用的场景

    1. 跨系统数据传递
    2. 高并发的流量削峰
    3. 数据的并发和异步处理
    4. 大数据分析与传递
    5. 分布式事务

    比如你有一个数据要进行迁移或者请求并发过多的时候,比如你有10 W的并发请求下订单,我们可以在这些订单入库之前,我们可以把订单请求堆积到消息队列中,让它稳健可靠的入库和执行

    在这里插入图片描述
    在这里插入图片描述

    8. 常见的消息中间件

    ActiveMQ、RabbitMQ、Kafka、RocketMQ等

    9. 消息中间件的本质及设计

    它是一种接受数据、接受请求、存储数据、发送数据等功能的技术服务

    MQ消息队列:负责数据的传接受,存储和传递,所以性能要高于普通服务和技术
    在这里插入图片描述
    谁来生产消息,存储消息和消费消息呢?
    在这里插入图片描述

    10. 消息中间件的核心组成部分

    1. 消息的协议
    2. 消息的持久化机制
    3. 消息的分发策略
    4. 消息的高可用,高可靠
    5. 消息的容错机制

    11. 小结

    其实不论选择单体架构还是分布式架构都是项目开发的一个阶段,在什么阶段选择合适的架构方式,而不能盲目追求,最后造成的后果和问题都需要自己买单。但作为一个开发人员学习和探讨新的技术使我们每个程序开发者都应该去保持和思考的问题。当我们没办法去改变社会和世界的时候,我们为了生活和生存那就必须要迎合企业和市场的需求,发挥你的价值和所学的才能,创造价值和实现自我。

    展开全文
  • node中间件本质上就是在进入具体的业务处理之前,先让特定过滤器处理。如下图所示: 我们目前看到的主流nodejs框架, 比如connect, koa, express, egg, nest等, 都离不开中间件的设计概念, 所以为了能让大家更...

    精彩回顾

    前言

    中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。

    在NodeJS中,中间件主要是指封装http请求细节处理的方法。我们都知道在http请求中往往会涉及很多动作, 如下:

    • IP筛选

    • 查询字符串传递

    • 请求体解析

    • cookie信息处理

    • 权限校验

    • 日志记录

    • 会话管理中间件(session)

    • gzip压缩中间件(如compress)

    • 错误处理

    当然还有很多自定义的处理动作. 对于Web应用而言,我们并不希望了解每一个细节性的处理工作,而是希望能够把主要精力集中在业务的开发上,以达到提升开发效率的目的, 所以引入了Node中间件来简化和封装这些基础逻辑处理细节.

    node中间件本质上就是在进入具体的业务处理之前,先让特定过滤器处理。如下图所示: 

    我们目前看到的主流nodejs框架, 比如connect, koa, express, egg, nest等, 都离不开中间件的设计概念, 所以为了能让大家更深入的窥探nodejs世界, 我们就非常有比较研究中间件的实现原理.

    正文

    在了解node中间件的概念之后, 我们就来手动实现一下中间件, 最后我们会简单分析一下koa中中间件的实现思路. 文章大纲如下:

    • node中间件核心原理实现

    • koa中间键实现方式

    • 利用koa中间件机制实现一个自己的koa中间件

    node中间件核心原理实现

    由上文介绍可知中间件是从http请求开始到响应结束过程中的处理逻辑,通常需要对请求和响应进行处理. 我们在实现node中间件模式时还需要考虑的一个问题就是多中间件共存的问题, 我们要思考如何将多个中间件的执行自动化, 不然在请求到响应的过程中只会执行最开始的中间件, 所以我们基本的中间件形式如下:

    const middleware = (req, res, next) => {
      // 请求处理逻辑
      next()
    }
    
    复制代码
    

    接下来我们先写个简单的案例来看看中间件是如何实现的.

    // 定义几个中间间函数
    const m1 = (req, res, next) => {
      console.log('m1 run')
      next()
    }
    
    const m2 = (req, res, next) => {
      console.log('m2 run')
      next()
    }
    
    const m3 = (req, res, next) => {
      console.log('m3 run')
      next()
    }
    
    // 中间件集合
    const middlewares = [m1, m2, m3]
    
    function useApp (req, res) {
      const next = () => {
        // 获取第一个中间件
        const middleware = middlewares.shift()
        if (middleware) {
          middleware(req, res, next)
        }
      }
      next()
    }
    
    // 第一次请求流进入
    useApp()
    
    复制代码
    

    由以上代码我们就不难发现next的作用了, 也就是实现自动调用中间件链的关键参数. 打印结果如下:

    m1 run
    m2 run
    m3 run
    
    复制代码
    

    以上即实现了基本中间件的执行模式, 但是我们还需要考虑异步的问题, 如果中间件还依赖第三发模块或者api的支持, 比如验证, 识别等服务, 我们需要在该异步中间件的回调里执行next, 才能保证正常的调用执行顺序, 如下代码所示:

    const m2 = (req, res, next) => {
      fetch('/xxxxx').then(res => {
    	next()
      })
    }
    
    复制代码
    

    还有一种中间件场景, 比如说日志中间件, 请求监控中间件, 它们会在业务处理前和处理后都会执行相关逻辑, 这个时候就要求我们需要能对next函数进行二次处理, 我们可以将next的返回值包装成promise, 使得其在业务处理完成之后通过then回调来继续处理中间件逻辑. 如下所示:

    function useApp (req, res) {
      const next = () => {
        const middleware = middlewares.shift()
        if (middleware) {
          // 将返回值包装为Promise对象
          return Promise.resolve(middleware(req, res, next))
        }else {
          return Promise.resolve("end")
        }
      }
      next()
    }
    
    复制代码
    

    此时我们就能使用如下方式调用了:

    const m1 = (req, res, next) => {
      console.log('m1 start')
      return next().then(() => {
        console.log('m1 end')
      })
    }
    
    复制代码
    

    以上我们就实现了一个基本可以的中间件设计模式, 当然我们也可以用async和await实现, 写法会更优雅和简单. 笔者这里上一份简单的例子:

    const m1 = async (req, res, next) => {
        // something...
        let result = await next();
      }
      
      const m2 = async (req, res, next) => {
        // something...
        let result = await next();
      }
      const m3 = async (req, res, next) => {
        // something...
        let result = await next();
        return result;
      }
    
    const middlewares = [m1, m2, m3];
    
    function useApp (req, res) {
        const next = () => {
          const middleware = middlewares.shift()
          if (middleware) {
            return Promise.resolve(middleware(req, res, next))
          }else {
            return Promise.resolve("end")
          }
        }
        next()
      }
    // 启动中间件
    useApp()
    
    复制代码
    

    在koa2框架中, 中间件的实现方式也是将next()方法返回值封装为Promise对象,实现了其提出的洋葱圈模型,如下图所示: 

    koa中间件实现方式

    koa2框架的中间件实现原理很优雅,笔者觉得很必要研究一下, 这里展示一下其核心思路:

    function compose (middleware) {
      // 提前判断中间件类型,防止后续错误
      if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
      for (const fn of middleware) {
        // 中间件必须为函数类型
        if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
      }
      return function (context, next) {
        // 采用闭包将索引缓存,来实现调用计数
        let index = -1
        return dispatch(0)
        function dispatch (i) {
          // 防止next()方法重复调用
          if (i <= index) return Promise.reject(new Error('next() called multiple times'))
          index = i
          let fn = middleware[i]
          if (i === middleware.length) fn = next
          if (!fn) return Promise.resolve()
          try {
            // 包装next()返回值为Promise对象
            return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
          } catch (err) {
            // 异常处理
            return Promise.reject(err)
          }
        }
      }
    }
    
    复制代码
    

    利用koa中间件机制实现一个自己的koa中间件

    学习了中间件的设计机制和原理, 我们是不是想马上写一个中间件呢? 笔者这里给大家举一个例子. 在H5-Dooring项目的服务端代码中, 我们需要对用户登录权限进行分发, 此时我们提供统一个中间件来处理, 如下代码所示:

    // 模拟数据库操作
    const token = db.user();
    
    // router或者koa的中间件一定要用await处理next,否则将不能正常响应数据
    export default async (ctx, next) => {
      const t = ctx.request.header.authorization
      let uid = ctx.request.header['x-requested-with']
      let uidArr = uid.split(',')
      if(uidArr.length > 1) {
          uid = uidArr.pop().trim()
      }
        if(token[uid] && token[uid][1] === t) {
            await next()
        }else {
            ctx.status = 403;
            ctx.body = {
                state: 403,
                msg: '你没有权限操作'
            }
        }  
    }
    
    复制代码
    

    以上代码即实现用户登录态处理, 如果用户在没有登录的情况下防问任何需要登录的接口, 都将返回权限不足或则在请求库中让其重定向到登录页面.

    所以, 今天你又博学了吗?

    最后

    笔者最近在迭代开源项目H5-Dooring,大家感兴趣的也可以研究交流一下.

    如果想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在《趣谈前端》一起学习讨论,共同探索前端的边界。

    点击左下角阅读原文, 为H5-Dooring投票!

    展开全文
  • 中间件-理解 对express而言,中间件是它的一个非常重要的概念,掌握中间件的思想对于理解学习express,提升编程水平都有很大的帮助。 生活中的中间件 一道复杂的任务拆分成几个小步骤: 步骤1 步骤2 步骤3 我们...
  • 本质就是一个自定义类,可以帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作 中间件可以定义五个方法: process_request(self,request) process_response(selef,request,response) process_view(self,...
  • 中间件】浅析深究什么是中间件

    千次阅读 2016-04-28 21:03:47
    中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相...
  • 中间件

    2019-09-12 10:49:35
    中间件 概念 引用:中间件解释,中间件系列 代码举例 var http = require('http') //这些就是中间件 var cookie = require('./middlewares/cookie') var postBody = require('./middlewares/post-body') var query ...
  • 文章目录中间件是什么为什么使用中间件主要中间件的分类1. Hadoop2. LVS3. Linux-HA4. 静态网站服务器5. 动态应用服务器DNS、DHCP、Kerberos和Radius:云计算基础服务组件1. DNS2. DHCP3. Kerberos1)身份认证2)...
  • 分布式中间件实践之路

    千次阅读 多人点赞 2018-11-06 11:49:02
    在分布式系统中,中间件是非常重要的组成,具有较高的技术门槛。同时,中间件相关的开源软件众多,适用场景各异,学习不易,对于初学者,本达人课无疑是值得一看的参考资料。 本课程是作者从事中间件研发的经验总结...
  • 文章目录前言一、内置中间件 (常用)
  • 数据库中间件详解

    千次阅读 2019-07-01 09:34:14
    数据库中间件详解 原创:田守枝田守枝的技术博客3月24日 1数据库拆分过程及挑战 互联网当下的数据库拆分过程基本遵循的顺序是:垂直拆分、读写分离、分库分表(水平拆分)。每个拆分过程都能解决业务上的一些问题...
  • Express 中间件

    2020-10-27 09:04:54
    1. Express 中间件 1.1 中间件的概念 1. 什么是中间件 中间件(Middleware ),特指业务流程的中间处理环节。 2. 现实生活中的例子 在处理污水的时候,一般都要经过三个处理环节,...Express 的中间件本质上就是一个
  • 刘润:中台的本质中间件

    千次阅读 2019-12-04 08:11:19
    当时为了弄明白为什么中国公司做不好“中间件”,我们特地去了可能是全世界最好的“中间件”公司SAP去交流。 他们不帮客户做定制,只是把那些可能不同的地方,做成了一个个插件,做成无数标准的模块。他们就是IT界的...
  • 一、是什么中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享...
  • python 中间件

    2020-12-09 03:21:41
    每个中间件组件都负责做一些特定的功能,中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在处理请求的特定的时间去执行这些方法...
  • 数据库中间件

    千次阅读 2017-12-11 23:52:49
    这里主要介绍互联网行业内有关数据库的相关中间件。数据库相关平台主要解决以下三个方面的问题: 为海量前台数据提供高性能、大容量、高可用性的访问为数据变更的消费提供准实时的保障高效的异地数据同步 应用层...
  • 中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在处理请求的特定的时间去执行这些方法。使用过多的中间件会影响性能 我们一直...
  • redux中间件详解

    2021-07-30 11:33:13
    redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如...
  • Redux 中间件

    2021-02-10 17:20:08
    中间件本质是就是一道函数,redux允许我们通过中间件的方式扩展和增强redux的应用,体现在action的处理逻辑上, 加载中间件之后,action会先被中间件处理,中间件在返回给reducer 开发redux的中间件 源码地址:...
  • 中间件实质为一个服务端,通过http请求进行通信,任何编程语言都可以对接。 提供简单的SDK(e、python、php、java),其他语言可自行组装http请求进行通讯。 初衷与目的: mysql数据库的增删改查基本是后端编程的...
  • 消息中间件本质是两个进程之间的通讯,像HTTP、RPC、Webservice都可以进行进程间通讯,那么我们为什么还需要消息中间件这个组件呢? 我们考虑一个场景,假如有一个交易系统,交易成功之后需要发送短信通知,如果...
  • 系统中间件回顾

    2018-07-03 17:23:53
    中间件定义:顾名思义,中间件就是处于中间的软件。但这种不是从功能,或者特性来定义的概念,而是用位置来定义的名字,就容易被不同的人从不同角度赋予其不同的含义。 IDC曾经给中间件下的定义是中间件是一种独立的...
  • Go middleware中间件

    2021-03-03 11:26:54
    先从业务开发角度出发,来逐渐引出中间件。一、刚开始时业务开发开始业务开发时,业务需求比较少。当我们最开始进行业务开发时,需求不是很多。 第一个需求产是品向大家打声招呼:“hello world”。接到需求任务,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,594
精华内容 19,837
关键字:

中间件的本质