精华内容
下载资源
问答
  • RocketMQ局部顺序消息实现原理

    千次阅读 2019-06-05 14:26:10
    顺序消息分为全局顺序消息和局部顺序消息,全局顺序消息是指:在某个topic下的所有消息都要保证消费顺序与产生顺序相同;部分顺序消息是指:只要保证每一组消息被顺序消费即可。在RocketMQ中,若要实现全局顺序消息...

    顺序消息:是指消息的消费顺序与消息的产生顺序相同;顺序消息分为全局顺序消息和局部顺序消息,全局顺序消息是指:在某个topic下的所有消息都要保证消费顺序与产生顺序相同;部分顺序消息是指:只要保证每一组消息被顺序消费即可。在RocketMQ中,若要实现全局顺序消息,首先把topic的读写队列设置为一,然后把生产者producer和消费者consumer都设置成单线程即可。但这样一来,就需要牺牲高并发和高吞吐量了。一般情况下,根据业务的需要,我们只需要实现局部顺序消息即可。

    在高并发情况下,RocketMQ实现局部顺序消息是通过消息的生产者和消息的消费者协同完成的。发送端需要做的事情:把同一个小组内的消息发送到指定的队列Message Queue中;消费端需要做的事情:仅用一个线程处理这个队列中的消息。

    默认情况下,消息的生产端实现负载均衡的做法是:轮流向各个消息队列Message Queue中发送消息。消息的消费端实现负载均衡的做法是:把消息队列的总数简单的除以消费者的个数,每个消费者负责一些消息队列(注意:消费者的数量不要超过消息队列的个数,否则多余的消费者接收不到消息)。在我们人为不干涉的情况下,把一条消息投递到哪个队列以及被哪个消费者下的线程消费都是未知的。

    为了实现局部顺序消息的消费,发送端通过使用MessageQueueSelector类来控制把消息发往哪个消息队列Message Queue中,其代码如下:

    SendResult result = null;
    try {
        result = producer.send(message, new MessageQueueSelector() {
            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                Integer queueNumber = (Integer)arg;
                return mqs.get(queueNumber);
            }
        }, 2);
    } catch (MQClientException e) {
        e.printStackTrace();
    } catch (RemotingException e) {
        e.printStackTrace();
    } catch (MQBrokerException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(result);

    在我们初始化消费者时,需要指定监听器的类型:

    MessageListenerOrderly:在消息需要按局部顺序消费时使用;

    MessageListenerConcurrently:在消息不需要按局部顺序消费时使用。

    在MessageListenerOrderly的实现中,为每个Consumer Queue加个锁,消费每个消息前,需要先获得这个消息所在的Consumer Queue所对应的的锁,这样就可以保证在同一时间、同一个Consumer Queue的消息不被并发消费,但不同的Consumer Queue的消息可以并发处理。

    为了实现局部顺序消息的消费,消息的消费端需要指定监听器类型为:MessageListenerOrderly,代码如下:

    this.consumer.setMessageListener(new MessageListenerOrderly() {
        public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
            try {
                //处理业务逻辑
                return ConsumeOrderlyStatus.SUCCESS;
            } catch (Exception e) {
                e.printStackTrace();
                //当消费消息的过程中,若是出现了异常,则稍后再重新消费
                return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
            }
        }
    });
    展开全文
  • 不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。Qt 保证的是,任何对象树中的 QObject 对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的 children() 列表中...

    当一个 QObject 对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。Qt 保证的是,任何对象树中的 QObject 对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的 children() 列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有 QObject 会被 delete 两次,这是由析构顺序决定的。


     

    如果 QObject 在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:

    1. {
    2.     QWidget window;
    3.     QPushButton quit("Quit", &window);
    4. }

    复制代码

    作为父组件的 window 和作为子组件的 quit 都是 QObject 的子类(事实上,它们都是 QWidget 的子类,而 QWidget 是 QObject 的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++ (ISO/IEC 14882:2003)要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。


     

    但是,如果我们使用下面的代码:

    1. {
    2.     QPushButton quit("Quit");
    3.     QWidget window;
    4.  
    5.     quit.setParent(&window);
    6. }

    复制代码

    情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序退出时崩溃了。

    展开全文
  • 多久不用C++ ,好多東西都模糊了,最近跟人討論全局和靜態... 全局与静态对象析构的顺序class A destructor delete a; class B destructor 局部变量 class D destructor // 局部静态变量 class C destructor // 全局

    本文章由cartzhang编写,转载请注明出处。 所有权利保留。
    文章链接:http://blog.csdn.net/cartzhang/article/details/72904337
    作者:cartzhang

    多久不用C++ ,好多東西都模糊了,最近跟人討論全局和靜態的析構順序,之前總覺得這個根據編譯器來決定的,其實還是有一定的說法的。
    記錄一下:

    1. 全局与静态对象析构的顺序

    class A destructor delete a;
    class B destructor 局部变量
    class D destructor // 局部静态变量
    class C destructor // 全局变量。

    结果显示:确实是全局变量比局部静态变量后析构。局部静态变量先析构,然后才是全局变量。

    2.全局静态变量

    那全局静态变量呢,他们应该与都在静态存储区啊?

    经过测试,

    static E e2;
     C c;
    int main()
    {
        A *a = new A();
        B b;
    
        static D d;
        delete a;
        a = NULL;
        return 0;
    }

    class A destructor
    class B destructor
    class D destructor
    class C destructor
    class E destructor

    ///
     C c;
     static E e2;
    
    int main()
    {
        A *a = new A();
        B b;
    
        static D d;
        delete a;
        a = NULL;
        return 0;
    }

    class A destructor
    class B destructor
    class D destructor
    class E destructor
    class C destructor。

    也就是全局变量和全局静态变量,都是最后析构,这只是一次测试结果,想到之前对于全局静态在不同编译器下初始化先后顺序不同,这个析构顺序也是偶然的。

    也就是说,C++全局静态变量的析构销毁顺序是未定义的,特别是在多线程环境,多dll环境下,特别复杂,但是在某个编译器下,析构顺序是一定,按着构造的反顺序析构,其他都是随机,不确定的。
    也可以这么理解,==全局变量和全局静态变量的析构顺序也是未定义的==。

    3.为什么局部静态变量比全局先析构呢?

    很明显,局部静态变量在程序第一次使用的时候进行构造,构造时间较晚,就较早析构,比其他全局变量或静态变量析构的早。

    4.全部测试程序

    // test.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <iostream>
    #include <string>
    #include <fstream>
    using namespace std;
    
    
    #define  D_Out(x) \
    cout << (x);\
    ofstream outfile;\
    outfile.open("testDestructor.txt", std::ofstream::app);\
    outfile << (x) << endl;\
    outfile.close();
    
    
    class  A
    {
    public:
         A();
        ~ A();
    
    private:
    
    };
    
     A:: A()
    {
         D_Out("class A constructor")
    }
    
     A::~ A()
    {
         D_Out("class A destructor")
    }
    
     class B
     {
     public:
         B();
         ~B();
    
     private:
    
     };
    
     B::B()
     {
         D_Out("class B constructor")
     }
    
     B::~B()
     {
         D_Out("class B destructor")
     }
    
     class C 
     {
     public:
         C ();
         ~C ();
    
     private:
    
     };
    
     C ::C ()
     {
         D_Out("class C constructor")
     }
    
     C ::~C ()
     {
         D_Out("class C destructor")
     }
    
     class D
     {
     public:
         D();
         ~D();
    
     private:
    
     };
    
     D::D()
     {
         D_Out("class D constructor")
     }
    
     D::~D()
     {
         D_Out("class D destructor")
     }
    
     class E
     {
     public:
         E();
         ~E();
    
     private:
    
     };
    
     E::E()
     {
         D_Out("class E constructor")
     }
    
     E::~E()
     {
         D_Out("class E destructor")
     }
    
    
     static E e2;
     C c;
    int main()
    {
        A *a = new A();
        B b;
    
        static D d;
        delete a;
        a = NULL;
        return 0;
    }

    结果:

    class E constructor
    class C constructor
    class A constructor
    class B constructor
    class D constructor
    
    class A destructor
    class B destructor
    class D destructor
    class C destructor
    class E destructor

    参考:

    【1】https://stackoverflow.com/questions/469597/destruction-order-of-static-objects-in-c
    【2】http://www.cnblogs.com/to-be-st/archive/2012/12/08/2808408.html

    展开全文
  • 局部变量入栈顺序与输出关系

    千次阅读 2017-05-25 11:33:59
    **C语言中入栈顺序与变量输出**1).内存区域划分: 图1 程序运行时的内存区域  如图所示:C程序中,栈区主要存储函数的参数,局部变量等,并且栈底为高地址,栈顶为低地址(如图:由高地址向低地址扩展)。

    C语言中入栈顺序与变量输出

    1).内存区域划分:

    这里写图片描述

    图1 程序运行时的内存区域

           如图所示:C程序中,栈区主要存储函数的参数,局部变量等,并且栈底为高地址,栈顶为低地址(如图:由高地址向低地址扩展)。

    2).入栈顺序:

    A:函数参数的入栈顺序:自右向左

    原因:
           函数参数的入栈顺序和具体编译器的实现有关。有些参数是从左向右入栈,如:Pascal语言从左到右入栈(不支持变参),被调用者清栈;有些语言还可以通过修饰符进行指定,如:Visual C++;但是C语言(cdecl)采用自右向左的方式入栈,调用者清栈。
    这是因为自右向左入栈顺序的好处就是可以动态的变化参数个数。通过堆栈分析可知,自左向右入栈方式中,最前面的参数会被压入栈底。除非知道参数个数,否则无法通过栈指针的相对位移求得最左边的参数。这样就无法实现可变参数。因此,C语言采用自右向左入栈顺序,主要是因为实现可变长参数形式(如:printf函数)。可变长参数主要通过第一个定参数来确定参数列表,所以自右向左入栈后,函数调用时栈顶指针指向的就是参数列表的第一个确定参数,这样就可以了。
    例子1:

    #include <stdio.h>
    
    void print(int x, int y, int z)
    {
        printf("x = %d addr %p\n", x, &x);
        printf("y = %d addr %p\n", y, &y);
        printf("z = %d addr %p\n", z, &z);
    }
    
    int main()
    {
        print(1,2,3);//自右向入压栈
        return 0;
    }

    运行结果:

    x = 1 addr 0xbfb5c760 //栈顶,后压栈
    y = 2 addr 0xbfb5c764
    z = 3 addr 0xbfb5c768 //栈底,先入栈

    B:局部变量的入栈顺序:

           在没有栈溢出保护机制下编译时,所有局部变量按系统为局部变量申请内存中栈空间的顺序,即:先申请哪个变量,哪个先入栈,正向的。也就是说,编译器给变量空间的申请是直接按照变量申请顺序执行的。(见例子2)
    在有栈溢出保护机制下编译时,入栈顺序有所改变,先按照类型划分,再按照定义变量的先后顺序划分,即:char型先申请,int类型后申请(与编译器溢出保护时的规定相关);然后栈空间的申请顺序与代码中变量定义顺序相反(后定义的先入栈)。(见例子2)
    例子2:stack.c

    #include <stdio.h>
    
    int main()
    {
        int a[5] = {1,2,3,4,5};
        int b[5] = {6,7,8,9,10};
        char buf1[6] = "abcde";
        char buf2[6] = "fghij";
        int m = -1;
        int n = -2;
        printf("a[0]    = %3d, addr: %p\n", a[0], &a[0]);
        printf("a[4]    = %3d, addr: %p\n", a[4], &a[4]);
        printf("b[0]    = %3d, addr: %p\n", b[0], &b[0]);
        printf("b[4]    = %3d, addr: %p\n", b[4], &b[4]);
        printf("buf1[0] = %3d, addr: %p\n", buf1[0], &buf1[0]);
        printf("buf1[5] = %3d, addr: %p\n", buf1[5], &buf1[5]);
        printf("buf2[0] = %3d, addr: %p\n", buf2[0], &buf2[0]);
        printf("buf2[5] = %3d, addr: %p\n", buf2[5], &buf2[5]);
        printf("m       = %3d, addr: %p\n", m, &m);
        printf("n       = %3d, addr: %p\n", n, &n);
    }

    没有栈溢出保护机制下的编译:

    $ gcc stack.c -g -o stack -fno-stack-protector
    $ ./stack
    a[0]    =   1, addr: 0xbfa5185c //数组内部,地址由低到高不变
    a[4]    =   5, addr: 0xbfa5186c //栈底,高地址
    b[0]    =   6, addr: 0xbfa51848
    b[4]    =  10, addr: 0xbfa51858
    buf1[0] =  97, addr: 0xbfa51842
    buf1[5] =   0, addr: 0xbfa51847
    buf2[0] = 102, addr: 0xbfa5183c
    buf2[5] =   0, addr: 0xbfa51841
    m       =  -1, addr: 0xbfa51838
    n       =  -2, addr: 0xbfa51834  //栈顶,低地址

    可以看出入栈顺序:a -> b -> buf1 -> buf2 -> m -> n(先定义,先压栈)
    栈溢出保护机制下的编译:

    $ gcc stack.c -g -o stack
    $ ./stack
    a[0]    =   1, addr: 0xbfc69130 //栈顶
    a[4]    =   5, addr: 0xbfc69140
    b[0]    =   6, addr: 0xbfc69144
    b[4]    =  10, addr: 0xbfc69154 
    buf1[0] =  97, addr: 0xbfc69160 //char类型,优先入栈
    buf1[5] =   0, addr: 0xbfc69165
    buf2[0] = 102, addr: 0xbfc69166
    buf2[5] =   0, addr: 0xbfc6916b //栈底
    m       =  -1, addr: 0xbfc69158
    n       =  -2, addr: 0xbfc6915c //int类型,后压栈

    可以看出入栈顺序:buf2 -> buf1 -> n -> m -> b -> a(char类型先入栈,int类型后入栈;先定义,后压栈)

    3).指针越界输出:

    例子3:stack1.c

    #include <stdio.h>
    
    int main()
    {
        char buf1[6] = "abcef";
        char buf2[6] = "fghij";
        int a[5] = {1,2,3,4,5};
        int b[5] = {6,7,8,9,10};
        int m = -1;
        int n = -2;
        char *p = &buf2[0];
        printf("a[0]    = %3d, addr: %p\n", a[0], &a[0]);
        printf("a[4]    = %3d, addr: %p\n", a[4], &a[4]);
        printf("b[0]    = %3d, addr: %p\n", b[0], &b[0]);
        printf("b[4]    = %3d, addr: %p\n", b[4], &b[4]);
        printf("buf1[0] = %3d, addr: %p\n", buf1[0], &buf1[0]);
        printf("buf1[5] = %3d, addr: %p\n", buf1[5], &buf1[5]);
        printf("buf2[0] = %3d, addr: %p\n", buf2[0], &buf2[0]);
        printf("buf2[5] = %3d, addr: %p\n", buf2[5], &buf2[5]);
        printf("m       = %3d, addr: %p\n", m, &m);
        printf("n       = %3d, addr: %p\n", n, &n);
        printf("p[0]    = %3d, addr: %p\n", p[0], &p[0]);
        printf("p[6]    = %3d, addr: %p\n", p[6], &p[6]);
        printf("p[-6]   = %3d, addr: %p\n", p[-6], &p[-6]);
        printf("p[-42]  = %3d, addr: %p\n", p[-42], &p[-42]);
        printf("p[-43]  = %3d, addr: %p\n", p[-43], &p[-43]);
        printf("p[-53]  = %3d, addr: %p\n", p[-53], &p[-53]);
        printf("p[-54]  = %3d, addr: %p\n", p[-54], &p[-54]);
        printf("p[-55]  = %3d, addr: %p\n", p[-55], &p[-55]);
        printf("p[-56]  = %3d, addr: %p\n", p[-56], &p[-56]);
        printf("p[-57]  = %3d, addr: %p\n", p[-57], &p[-57]);
        printf("p[-58]  = %3d, addr: %p\n", p[-58], &p[-58]);
        printf("p[-59]  = %3d, addr: %p\n", p[-59], &p[-59]);
    }

    栈溢出保护机制下的编译:

    $ gcc stack1.c -g -o stack1
    $ ./stack1
    a[0]    =   1, addr: 0xbff5ab6c //栈顶,0xbff5ab6c,低地址
    a[4]    =   5, addr: 0xbff5ab7c
    b[0]    =   6, addr: 0xbff5ab80
    b[4]    =  10, addr: 0xbff5ab90
    buf1[0] =  97, addr: 0xbff5aba0 //&p[-6]
    buf1[5] =   0, addr: 0xbff5aba5
    buf2[0] = 102, addr: 0xbff5aba6 //&p[0]
    buf2[5] =   0, addr: 0xbff5abab //栈底,0xbff5abab,高地址--->&p[6]:越界,值随机
    m       =  -1, addr: 0xbff5ab94
    n       =  -2, addr: 0xbff5ab98
    p[0]    = 102, addr: 0xbff5aba6 //&buf2[0]
    p[6]    =   0, addr: 0xbff5abac //&buf2[6],越界,无初始值,值随机
    p[-6]   =  97, addr: 0xbff5aba0 //&buf1[0],越界,已有初始值,buf1[0],p[-6]为97
    p[-42]  =   5, addr: 0xbff5ab7c //&a[4]
    p[-43]  =   0, addr: 0xbff5ab7b //&a[4] - 1字节,大小0x00 = 0
    p[-53]  =   0, addr: 0xbff5ab71 //&a[1] + 1字节,大小0x00 = 0
    p[-54]  =   2, addr: 0xbff5ab70 //&a[1]
    p[-55]  =   0, addr: 0xbff5ab6f //p[-55]到p[-58]能看出Linux是小端存储。
    p[-56]  =   0, addr: 0xbff5ab6e //小端存储:低地址存低位,高地址存高位
    p[-57]  =   0, addr: 0xbff5ab6d //a[0]=1,即:0x01 0x00 0x00 0x00(低位到高位)
    p[-58]  =   1, addr: 0xbff5ab6c //&a[0]
    p[-59]  =   -65, addr: 0xbff5ab6b //&a[0] - 1字节,越界,无初始值,值随机

    入栈顺序:
    (栈底:高地址)buf2 -> buf1 -> n -> m -> b -> a[4] -> a[0](栈顶:低地址)
             &p[6]—&p[0]—&p[-6]——————&p[-42]—&p[-58]—&p[-59]
    提醒:指针p越界会出现问题,如果在p[-6] = ‘k’;那么会导致因越界覆盖内存里面buf1[0]的值。

    展开全文
  • 局部变量入栈顺序与变量输出关系1).内存区域划分:图1 程序运行时的内存区域如图所示:C程序中,栈区主要存储函数的参数,局部变量等,并且栈底为高地址,栈顶为低地址(如图:由高地址向低地址扩展)。2).入栈顺序...
  • 首先分配基本变量,按字节大小,...A:函数参数的入栈顺序:自右向左 原因: 函数参数的入栈顺序和具体编译器的实现有关。有些参数是从左向右入栈,如:Pascal语言从左到右入栈(不支持变参),被调用者清栈;有些...
  • public class Judge { public static void main(String[] args) { ...//执行顺序如输出语句标识 } static class TestOne{ static{ System.out.println("1.静态代码块"); } ...
  • 静态对象是指具有静态存储期限的对象,即从定义式开始,分配的...对于在同一个编译单元(产生单一目标文件的源码,由单一源文件和其包含的头文件构成)定义的非静态局部对象,它们的初始化顺序是由其定义顺序决定的,而
  • static T fsob("外部局部静态对象static fsob of fun");//外部局部静态对象 cout()"; } int main() { T mob1("主函数局部对象1 mob1 of main");//主函数局部对象1 static T msob("主函数局部静态对象 static ...
  • C语言局部变量在内存栈中的顺序

    千次阅读 2017-08-23 20:44:10
    规则2:同总占位的类型按定义变量的先后顺序内存地址会增加 规则3:在规则2前提下,定义数组不会和同总数据类型混占内存 下面看示例程序: 示例程序 1 : #include int main( int argc, const ...
  • 今天,因为工作需要,遇到一个可能很基础但是又比较模糊的知识点,局部静态变量和全局变量的释放顺序到底是怎么样的呢? 开门见山的说,遵循先声明后释放原则。 但是,怎么知道谁先声明的呢?全局变量声明定义...
  • 析构先后顺序为:局部变量、mian函数中注册atexit函数、全局变量及在其中注册atexit函数(全局变量和在全局变量中注册atexit函数析构顺序与构造顺序相反)。测试代码如下(gcc-4.6.3编译, ubuntu 12.04 linux 内核...
  • kafka顺序消息

    千次阅读 2018-08-09 19:33:53
    局部顺序 全局顺序 全局顺序就目前的应用范围来讲,可以列举出来的也就限于binlog日志传输,如mysql binlog日志传输要求全局的顺序,不能有任何的乱序。这种的解决办法通常是最为保守的方式: 全局使用一个生产者...
  • c语言局部变量的内存地址分配顺序

    千次阅读 2018-08-31 16:04:46
    测试程序: #include &amp;lt;stdio.h&amp;gt; int main(){ unsigned int ui = 10; char c = 11; unsigned char uc = 12; int i = 13; double d = 14; short s = 15;... print...
  • 局部变量中,我们知道变量是存放在栈中的,所以变量的释放次序应该是后声明的先释放内存,现在我们看一看全局变量之间,以及全局变量与静态变量之间的内存释放顺序 代码如下: #include &amp;amp;amp;lt;...
  • RocketMQ-顺序消息Demo及实现原理分析

    千次阅读 多人点赞 2019-05-31 19:30:30
    文章目录场景分析全局顺序消费局部顺序消费DemoProducer DemoNormal Consumer DemoOrder Consumer Demo源码分析锁定MessageQueue客户端实现broker端实现锁定ProcessQueue 场景分析 顺序消费是指消息的产生顺序和消费...
  • 我们知道在C++中,全局变量和局部变量的存储区域不同,全局变量存放在静态数据区,局部变量存放在栈区。但还有一个小点就是存放在静态数据区的变量是由低地址向高地址存放的,但存放在栈区的变量却是由高地址向低...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 308,644
精华内容 123,457
关键字:

局部顺序