精华内容
下载资源
问答
  • 动态消息举例
    千次阅读
    2017-06-29 10:31:24

    对象抽象

    真实世界的对象有成千上万个属性,是不是把它们通通搬进计算机世界里来呢?回答显然是不行的,分析人员往往把那些只对用计算机管理业务所必须的对象属性映射到计算机里,而忽略那些无助于处理业务的无用信息。这个过程叫作对象抽象。

    封装

    封装是把对象的属性、操作结合在一起,构成一个独立的对象。一旦封装,内部信息对外界是隐藏的,也就象一个四周密封的房子一样,不允许直接对对象的属性进行操作,要想操作,只能通过局部接口(相当于房子的大门)。外部只能看到对象对操作的反应,而不知道对象是如何做出这一反应的。所以封装包含两个方面的含义,一个是信息隐藏,另一个局部开放。

    消息

    在系统中的对象只有对外提供服务,才能发挥自己的作用,当系统中的其他对象请求这个对象提供服务时,该对象就给予响应,并完成指定的操作。在这个过程中,其他对象要求提供服务这个信息就叫作消息。

    聚合

    如果对象间是一种比较松散的关系,整体对象和部分对象可以独立地创建,并在整体对象中设置一个部分对象的属性,它可以是部分对象的标识,也可以是指向部分对象的指针。同时,部分对象也可以属于多个整体对象,其生命周期与整体对象不同,这种对象间的关系叫聚合。

    多态

    如果在父类定义的属性和操作被子类继承以后,表现出不同的属性和操作,这种现象叫作多态。多态的基本表现是:属性名或操作名在子类和父类中相同,但语义不同。比如笔可以写字,但写什么样的字并没有确定,在执行时,子类钢笔、毛笔、粉笔都继承了笔的写字操作,但功能却不一样,钢笔写出钢笔字,毛笔写出毛笔字,粉笔写出粉笔字,毛笔子类无法写出钢笔字,同理,如果毛笔类再分为大字笔和小字笔两个子类,它们两个的功能又不一样……因此,对于同一个消息,让继承关系中的不同对象去执行,执行的结果也不同。
    多态的实现主要靠以下几个机制:

    1. 重载:即在子类中对继承来的属性或操作进行重新定义(俗称改写),有很多开发语言支持这一特性。

    2. 动态绑定:即消息的接收对象根据接收的消息,动态地确定调用自己的那个操作。

    3. 类属:即服务的参数类型是参数化的,根据这些参数类型,动态地确定调用自己的那个操作。

    4. 定义接口类:另一种实现多态的方式是定义一个特殊的抽象类(缺乏完整定义的类)接口类,它只声明一些操作标记,告诉其他对象如何触发该行为的细节,如名称,参数、返回值,再创建接口类多个子类,在每个子类中实现接口类定义的所有操作,从实现相同的接口类这个角度来看,表现出相同的一组接口由不同的子类去实现这样的多态。

    永久对象

    指生存周期可以超越程序的执行时间而长期存在的对象,又叫持久对象。永久对象与直接存储对象的区别在于,封装过后,进行序列化,把对象的属性、关联等信息直接保存到控件或文件中。永久对象的优势是屏蔽了共享内存和文件操作,方便程序员使用,此外,它还具有事务功能。

    主动对象

    主动对象是指不需要接收消息就可以主动执行操作的对象。主动对象内部包含一个线程,可以自动完成动作或改变状态,而一般的被动对象只能通过被其他对象调用才有所作为。在多线程程序中,经常把一个线程封装到主动对象里面。参考文章:http://blog.chinaunix.net/uid-20528042-id-1935065.html

    更多相关内容
  • 文章目录概述消息队列消息队列特性为什么需要消息队列消息队列的好处消息队列举例应用拆分应用拆分的原则应用拆分的思考应用拆分常用的组件DubboSpring Cloud应用限流限流算法 -- 计数器法 ,简单但是有临界问题限流...

    概述

    在这里插入图片描述

    这里只是讲通用的思路,实际高并发的场景需要根据实际情况来决定方案。

    在这里插入图片描述


    消息队列

    消息队列特性

    • 业务无关: 只做消息分发
    • FIFO : 先投递先到达
    • 容灾:节点的动态增删和消息的持久化
    • 性能: 吞吐量提升,系统内部通信效率提高

    为什么需要消息队列

    • 【生产】和【消费】的速度或稳定性等因素不一致

    消息队列的好处

    • 业务解耦
    • 最终一致性(要么都成功,要么都失败)
    • 广播,接入新的系统,只要需要确保把消息推送到消息队列即可,新系统从消息队列订阅即可
    • 错峰与流控

    消息队列举例

    Kafka

    在这里插入图片描述

    RabbitMQ

    在这里插入图片描述


    应用拆分

    应用拆分的原则

    • 业务优先
    • 循序渐进
    • 兼顾技术:重构、分层
    • 可靠测试

    应用拆分的思考

    • 应用之间的通信: RPC(Dubbo等)、消息队列
    • 应用之间的数据库设计:每个应用应该有独立的数据库
    • 避免事务操作跨应用

    应用拆分常用的组件

    Dubbo

    Spring Cloud

    在这里插入图片描述

    在这里插入图片描述


    应用限流

    在这里插入图片描述

    如果有大量的数据,在同一时间内直接写入数据库,势必对系统造成很大的压力。如果通过特定的方式采用限流的方式以很定的速率来写入数据库,那数据库压力就会小很多。


    以下算法是说的我们在业务代码中的逻辑限流

    限流算法 – 计数器法 ,简单但是有临界问题

    在这里插入图片描述

    假设有个接口A,规定1分钟的访问次数不能超过100次。通常的做法:设置一个计数器counter,每当一个请求过来的时候,counter就加1,

    如果counter>100并且该请求与第一个请求的间隔时间还在1分钟之内,那么说明请求数过多,触发限流

    如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置counter


    优点: 计数器法是限流算法里最简单也是最容易实现的一种算法。


    缺点 :临界问题

    假设有一个恶意用户,在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求,可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

    刚才的问题其实是因为我们统计的精度太低。那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降低呢?我们可以看下面的滑动窗口算法


    限流算法 – 滑动窗口 (Rolling Window),划分多个时间窗口解决临界问题

    在这里插入图片描述

    在上图中,整个红色的矩形框表示一个时间窗口,一个时间窗口就是一分钟。

    然后我们将时间窗口进行划分,如上图中,我们就将滑动窗口划成了6格,所以每格代表的是10秒钟。

    每过10秒钟,我们的时间窗口就会往右滑动一格。

    每一个格子都有自己独立的计数器counter,比如当一个请求在0:35秒的时候到达,那么0:30~0:39对应的counter就会加1。

    那么滑动窗口怎么解决刚才的临界问题的呢?我们可以看上图,0:59到达的100个请求会落在灰色的格子中,而1:00到达的请求会落在橘黄色的格子中。当时间到达1:00时,我们的窗口会往右移动一格,那么此时时间窗口内的总请求数量一共是200个,超过了限定的100个,所以此时能够检测出来触发了限流。

    再来看一下刚才的计数器算法,我们可以发现,计数器算法其实就是滑动窗口算法。只是它没有对时间窗口做进一步地划分,所以只有1格。

    由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。


    限流算法 – 漏桶(Leaky Bucket)

    在这里插入图片描述
    首先,有一个固定容量的桶,有水流进来,也有水流出去。对于流进来的水来说,我们无法预计一共有多少水会流进来,也无法预计水流的速度。但是对于流出去的水来说,这个桶可以固定水流出的速率。而且,当桶满了之后,多余的水将会溢出。

    我们将算法中的水换成实际应用中的请求,我们可以看到漏桶算法天生就限制了请求的速度。当使用了漏桶算法,我们可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题


    限流算法 – 令牌桶(Token Bucket)

    在这里插入图片描述

    首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的,token以一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。


    总结

    计数器 VS 滑动窗口

    计数器算法是最简单的算法,可以看成是滑动窗口的低精度实现。滑动窗口由于需要存储多份的计数器(每一个格子存一份),所以滑动窗口在实现上需要更多的存储空间。也就是说,如果滑动窗口的精度越高,需要的存储空间就越大。

    漏桶算法 VS 令牌桶算法

    漏桶算法和令牌桶算法最明显的区别是令牌桶算法允许流量一定程度的突发。因为默认的令牌桶算法,取走token是不需要耗费时间的,也就是说,假设桶内有100个token时,那么可以瞬间允许100个请求通过。

    令牌桶算法由于实现简单,且允许某些流量的突发,对用户友好,所以被业界采用地较多。当然我们需要具体情况具体分析,只有最合适的算法,没有最优的算法。


    展开全文
  • Cisco校园网设计方案举例 Cisco校园网设计方案举例
  • 聊聊RabbitMq动态监听这点事

    千次阅读 2022-02-22 16:29:07
    很长时间没有分享过学习心得了,看了下发布记录,...Mq(消息队列)做为一个消峰工具而常被使用,我们常用的Mq主要分为以下四种: ActiveMQ RabbitMq Kafka RocketMq 今天主要是聊聊RabbitMq,业务场景上选择Rab

    很长时间没有分享过学习心得了,看了下发布记录,最后一篇文章的时间都在2020-12-10年了,今天抽时间整理下一个很早就想整理的技术分享。顺便说句题外话,因为我一直没时间整理,再加上开发的小伙伴对Mq的理解不够,我开掉了好几个处理这个事情的开发小伙伴,所以我希望这篇文章能对大家带来一点帮助。

    背景说明

    Mq(消息队列)做为一个消峰工具而常被使用,我们常用的Mq主要分为以下四种:

    • ActiveMQ
    • RabbitMq
    • Kafka
    • RocketMq

    今天主要是聊聊RabbitMq,业务场景上选择RabbitMq的原因有很多,今天就不细说了。今天主要是说下如何动态创建队列,并实现动态监听的的方法。

    需求背景

    做为一个CRM-SAAS平台,每天平台会进入大量的客户信息,那么我们需要用高效的方式把这些数据及时的发给销售,那么这里需要考虑以下几个问题:

    1. 下发数据的及时性
    2. 数据分组
    3. 接收人员属于不同的分组和不同的级别
    4. 数据不满足下发条件(这里举个例子:接收人员都在忙的情况,可能需要过段时间重发)重发的问题

    技术方案

    1. 为保证数据及时性,数据进入系统之后及时推进消费队列
    2. 针对数据分组和接收人员不同的分组和不同的级别,并要人为可控的话,那么就设定不同的队列来进行监听消费,我们还可以让队列名称变得有意义,从队列名称上获取我们所需要某些必要信息,例如数据属于那个分组,数据应该下发的群体等。

    基于上述考虑,我们选择RabbitMq来实现这个方案,既然是不同的队列消费不同的数据,那么第一步就是考虑如何动态创建队列,因为这里还要设定一个人为可控,也就是人员可以管理,所以比然后伴随着队列的删除和重建。

    队列的创建方式

    基于注解的使用

        @Bean
        public Queue syncCdrQueue(){
            return new Queue(CrmMqConstant.SYNC_CDR_TO_CRM_Q,true,false,false);
        }

    非注解配置

    		Channel channelForm = connectionFactory().createConnection().createChannel(false);
    		channelForm.queueDeclare(nameForm, true, false, false, null);

    基于RabbitAdmin

    rabbitAdmin.declareQueue(queue);
    rabbitAdmin.declareExchange(fanoutExchange);
    rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(fanoutExchange));

    从创建队列的灵活度来说,肯定是依次减弱的:

    • 注解方式:提前定义队列名称,一般以常量来定义,当然也支持变量的方式,但是对于加载先后的要求就高了,例如:这里用一个动态IP作为队列名称举例
      private final String QUEUE_NAME="crm.websocket."+ IPUtils.getLocalhostIp();
      
          @Bean
          public Queue queue(){
              return new Queue(QUEUE_NAME,true,false,false);
          }
      
      //监听
      @RabbitListener(queues = "#{queue.name}")
    • 非注解方式:这个其实就是通过ConnectionFactory来获取通道创建队列的,这个比较适合在建立链接的时候使用,所以一般在批量初始化队列时候比较合适
        @Bean
        public List<String> mqMsgQueues() throws IOException {
        	List<String> queueNames = new ArrayList<String>();
        	List<Map<String,Object>> engineList = autoAssignEngineService.getAllAutoAssignEngine(-1,-1);
            logger.info("engineList:{}", JsonUtils.toJson(engineList));
        	if(engineList != null && engineList.size() > 0) {
        		for(Map<String,Object> engine : engineList) {
        			String groupId = String.valueOf(engine.get("orgId"));
        			String semAdType = String.valueOf(engine.get("semAdType"));
                    logger.info("groupId:{},semAdType:{}", groupId,semAdType);
    	            createQueue(queueNames, groupId,semAdType,"1");
    	            createQueue(queueNames, groupId,semAdType,"2");
        		}
        	}
        	return queueNames;
        }
    
    	private void createQueue(List<String> queueNames, String groupId, String semType, String level) throws IOException {
    		String nameForm = queue +"."+ groupId+"."+semType + "." + level;
    		logger.info("nameForm:{}",nameForm);
    		Channel channelForm = connectionFactory().createConnection().createChannel(false);
    		channelForm.queueDeclare(nameForm, true, false, false, null);
    		channelForm.exchangeDeclare(topicExchange, BuiltinExchangeType.TOPIC,true);
    		channelForm.queueBind(nameForm,topicExchange,routingKey + "."+groupId+"."+semType+"."+level);
    		queueNames.add(nameForm);
    	}
    • 基于RabbitAdmin的方式:那么这个就相对来说比较灵活,支持随时创建队列了。那么简单封装下:
         public void createMqQueue(String queueName,String exName,String rk,String type){
              Properties properties = rabbitAdmin.getQueueProperties(queueName);
              if(properties==null) {
                  Queue queue = new Queue(queueName, true, false, false, null);
                  if(BuiltinExchangeType.DIRECT.getType().equals(type)) {
                      DirectExchange directExchange = new DirectExchange(exName);
                      rabbitAdmin.declareQueue(queue);
                      rabbitAdmin.declareExchange(directExchange);
                      rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(directExchange).with(rk));
                  }else if(BuiltinExchangeType.FANOUT.getType().equals(type)){
                      FanoutExchange fanoutExchange = new FanoutExchange(exName);
                      rabbitAdmin.declareQueue(queue);
                      rabbitAdmin.declareExchange(fanoutExchange);
                      rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(fanoutExchange));
                  }else{
                      TopicExchange topicExchange = new TopicExchange(exName);
                      rabbitAdmin.declareQueue(queue);
                      rabbitAdmin.declareExchange(topicExchange);
                      rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(topicExchange).with(rk));
                  }
              }
          }

      我们知道如何动态创建队列之后,接下来我们想办法解决动态消费监听得事情就行:

    动态消费监听

    RabbitMq得抽象监听类是:AbstractMessageListenerContainer,他下面有三个实现类,这里就使用SimpleMessageListenerContainer类来进行简单得说明。

    方式一(我一个前辈得方式):

    初始化队列,存储在静态缓存,用不同得bean来加载监听:

    private List<Map<String,String>> groupOrgIds = new ArrayList<Map<String,String>>()
    @PostConstruct
    	public void init() {
    		if (logger.isDebugEnabled()) {
    			logger.debug("initbean...");
    		}
    		List<AutoAssignEngine> engineList = autoAssignEngineService.getAllAutoAssignEngine();
    		if (engineList != null && engineList.size() > 0) {
    			for(AutoAssignEngine engine : engineList) {
    				createQueueList(engine.getOrgId(),engine.getSemAdType(),"1");
    				createQueueList(engine.getOrgId(),engine.getSemAdType(),"2");
    			}
    		}
    	}
    private void createQueueList(String orgId,String semType,String userLevel) {
    		Map<String,String> feed = new HashMap<String, String>();
    		feed.put("orgId", orgId);
    		feed.put("type", semType);
    		feed.put("userLevel", userLevel);
    		groupOrgIds.add(feed);
    	}
    public SimpleMessageListenerContainer setContainer() {
    		SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    		container.setConnectionFactory(rabbitConfig.connectionFactory());
    		container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    		container.setMaxConcurrentConsumers(8);
    		container.setConcurrentConsumers(5);
    		container.setPrefetchCount(10);
    		return container;
    	}
    
    	public SimpleMessageListenerContainer queueMethod(SimpleMessageListenerContainer container) {
    	    Map<String,String> orgIdMap = groupOrgIds.get(0);
    		String orgId = orgIdMap.get("orgId");
    		String sourceType = orgIdMap.get("type");
    		String userLevel = orgIdMap.get("userLevel");
    		String queueNames=queueName + "." + orgId+"."+sourceType+"."+userLevel;
    		container.addQueueNames(queueNames);
    		excute(orgId,sourceType, container,queueNames);
    		groupOrgIds.remove(0);
    		return container;
    	}
    public SimpleMessageListenerContainer excute(String orgId,String semAdType, SimpleMessageListenerContainer container,String queneName) {
    		container.setMessageListener(new ChannelAwareMessageListener() {
    			@Override
    			public void onMessage(Message message, Channel channel) throws Exception {
    
    			}
    		});
    		return container;
    	}
    /**
    	 * 创建多个队列监听,利用Bean的初始化顺序,去消费groupOrgIds
    	 */
    	@Bean
    	public SimpleMessageListenerContainer container1() {
    		SimpleMessageListenerContainer container = setContainer();
    		queueMethod(container);
    		return container;
    	}
    @Bean
    	public SimpleMessageListenerContainer container2() {
    		SimpleMessageListenerContainer container = setContainer();
    		queueMethod(container);
    		return container;
    	}
    .....

    那么这种方式呢确实能动态监听不同得队列和消费,但是因为利用得是Bean得初始化得方式,所以每次变更需要加载得队列内容就得重新加载Bean,也就是需要重启服务。

    方式二:真正得动态监听

    	@Bean
    	public SimpleMessageListenerContainer simpleMessageListenerContainer() {
    		SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    		container.setConnectionFactory(rabbitConfig.connectionFactory());
    		container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    		container.setMaxConcurrentConsumers(8);
    		container.setConcurrentConsumers(5);
    		container.setPrefetchCount(10);
    		// 查询有多少分配引擎,每个分配引擎一个队列
    		List<AutoAssignEngine> engineList = autoAssignEngineService.getAllAutoAssignEngine();
    		if (engineList != null && engineList.size() > 0) {
    			for(AutoAssignEngine engine : engineList) {
    				mqService.addNewListener(engine.getOrgId(),engine.getSemAdType(),"1",container);
    				mqService.addNewListener(engine.getOrgId(),engine.getSemAdType(),"2",container);
    			}
    		}
    		return container;
    	}
    
     public Boolean addNewListener(String orgId,String semType,String userLevel,SimpleMessageListenerContainer container ){
            String queueNames=queueName + "." + orgId+"."+semType+"."+userLevel;
            container.addQueueNames(queueNames);
            container.setMessageListener(new ChannelAwareMessageListener() {
                @Override
                public void onMessage(Message message, Channel channel) throws Exception {
    
                }
            });
            return true;
        }

    问题1:这里再接收消息(onMessage方法内)得时候不要用方法传参,会出现并发问题。

    解决方式1:

    String receiveQueueName = message.getMessageProperties().getConsumerQueue();

    队列名称解析获取,本人使用。

    解决方式2:

    使用final变量重新接收传参,不过这个有待测试,不一定又用。

    问题2:这不是还是在Bean初始化得时候加载得嘛,如果想要在服务启动之后再增加监听如何处理

    完整得动态创建队列和监听(业务过程种实现)

    我们知道如何创建队列和监听之后就开始解决问题2。

    需求:变更现有队列。

    转化需求为:删除现有队列和监听,新建新得队列并增加监听

    问题:推送和消费不再统一服务。

    解决方式:暴露接口,利用http请求实现同步。

    代码实现:

    消费端

        public Boolean updateListener(String orgId,String semType,String oldOrg){
            logger.info("================================消费端开始处理");
            String newFirstQueueName = queueName+"."+orgId+"."+semType+"."+1;
            String newFirstRk = routingKey+"."+orgId+"."+semType+"."+1;
            String newSecondQueueName = queueName+"."+orgId+"."+semType+"."+2;
            String newSecondRk =  routingKey+"."+orgId+"."+semType+"."+2;
            createMqQueue(newFirstQueueName,topicExchange,newFirstRk, BuiltinExchangeType.TOPIC.getType());
            createMqQueue(newSecondQueueName,topicExchange,newSecondRk, BuiltinExchangeType.TOPIC.getType());
            logger.info("================================创建队列");
            SimpleMessageListenerContainer container = SpringCtxUtils.getBean(SimpleMessageListenerContainer.class);
            String oneQueueNames=queueName + "." + orgId+"."+semType+"."+1;
            String twoQueueNames=queueName + "." + orgId+"."+semType+"."+2;
            if(!"NO".equals(oldOrg)) {
                String oneOldQueueNames = queueName + "." + oldOrg + "." + semType + "." + 1;
                String twoOldQueueNames = queueName + "." + oldOrg + "." + semType + "." + 2;
                container.removeQueueNames(oneOldQueueNames);
                container.removeQueueNames(twoOldQueueNames);
                logger.info("================================删除监听成功");
            }
            container.addQueueNames(oneQueueNames);
            container.addQueueNames(twoQueueNames);
            logger.info("================================添加监听成功");
            container.setMessageListener(new ChannelAwareMessageListener() {
                @Override
                public void onMessage(Message message, Channel channel) throws Exception {
    
                }
            });
            return true;
        }
    
        public void createMqQueue(String queueName,String exName,String rk,String type){
            Properties properties = rabbitAdmin.getQueueProperties(queueName);
            if(properties==null) {
                Queue queue = new Queue(queueName, true, false, false, null);
                if(BuiltinExchangeType.DIRECT.getType().equals(type)) {
                    DirectExchange directExchange = new DirectExchange(exName);
                    rabbitAdmin.declareQueue(queue);
                    rabbitAdmin.declareExchange(directExchange);
                    rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(directExchange).with(rk));
                }else if(BuiltinExchangeType.FANOUT.getType().equals(type)){
                    FanoutExchange fanoutExchange = new FanoutExchange(exName);
                    rabbitAdmin.declareQueue(queue);
                    rabbitAdmin.declareExchange(fanoutExchange);
                    rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(fanoutExchange));
                }else{
                    TopicExchange topicExchange = new TopicExchange(exName);
                    rabbitAdmin.declareQueue(queue);
                    rabbitAdmin.declareExchange(topicExchange);
                    rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(topicExchange).with(rk));
                }
            }
        }
    

    暴露接口:

        @GetMapping("/add-listener/{orgId}/{semType}/{oldOrg}")
        public ComResponse addListener(@PathVariable("orgId") String orgId,@PathVariable("semType") String semType,@PathVariable("oldOrg") String oldOrg){
            mqService.updateListener(orgId,semType,oldOrg);
            return ComResponse.successResponse();
        }

    注意:执行顺序,创建新队列,删除监听,添加监听

    推送端

    //添加新得监听
            String requestUrl = consumerUrl+"/"+newOrg+"/"+semType+"/"+oldOrgId;
            String result = restTemplateService.getWithNoParams(requestUrl,String.class);
            log.info("请求结束:{}",result);
            if(!"NO".equals(oldOrgId)) {
                String firstQueueName = queue + "." + oldOrgId + "." + semType + "." + 1;
                String secondQueueName = queue + "." + oldOrgId + "." + semType + "." + 2;
                mqService.deleteMqQueue(firstQueueName);
                mqService.deleteMqQueue(secondQueueName);
                log.info("删除队列结束");
            }
            //新增新的的队列
            String newFirstQueueName = queue+"."+newOrg+"."+semType+"."+1;
            String newFirstRk = routingKey+"."+newOrg+"."+semType+"."+1;
            String newSecondQueueName = queue+"."+newOrg+"."+semType+"."+2;
            String newSecondRk =  routingKey+"."+newOrg+"."+semType+"."+2;
            mqService.createMqQueue(newFirstQueueName,topicExchange,newFirstRk, BuiltinExchangeType.TOPIC.getType());
            mqService.createMqQueue(newSecondQueueName,topicExchange,newSecondRk, BuiltinExchangeType.TOPIC.getType());
            log.info("添加队列结束");

    注意:执行顺序:变更监听,删除队列,添加新得队列

    到这里基本上就实现了动态创建队列和动态监听。大家如果有什么不太明白得可以留言,抽时间整理得,所以写得比较草,大家凑合着看把。

    展开全文
  • 这里我们主要学习系统设计,这篇博客我们学习系统动态建模中交互模型建模。    首先需要我们学习的是对象之间的通信,这里重要的一点就是消息的传递。消息分为简单信息,同步消息,异步消息和返回消息。根基消息...

     

          在这张图中,系统对象静态设计前边我们在分析中已经学习了,这个阶段需要做的就是细化优化。这里我们主要学习系统设计,这篇博客我们学习系统动态建模中交互模型建模。

     

           首先需要我们学习的是对象之间的通信,这里重要的一点就是消息的传递。消息分为简单信息,同步消息,异步消息和返回消息。根基消息的类型,我重点说两种操作,同步操作和异步操作。

     

          同步操作的过程:a,同步消息的发送者将进程控制传递给消息的接受者,暂停活动,等待同步消息接受者返回控制;b,同步消息的接受者执行所请求的操作,完成后将控制返回同步消息的发送者。

     

          异步操作的过程:a,异步消息的发送者是将消息发给消息的接受者,继续自身的活动,不等待消息接受者返回控制;b,异步消息的接受者执行所请求的操作,完成后将控制返回异步消息的发送者。

     

           通信是交互图中的重头戏,下边我们来看顺序图建模:

     

            顺序图(sequence diagram)用于描述对象之间的动态交互关系,主要体现对象之间进行消息传递的时间顺序。下边我们看一下组成:

     

     

         下边我们以课程管理系统中的修改课程为例来画顺序图:

     

     

         下边我们看另一种交互图——协作图:

     

          协作图(CollaborationDiagram):和顺序图一样用于描述相互合作对象之间的交互关系与连接关系,但是这里更侧重对象之间的关系。组成:

     

     

        下边我们以选课管理系统中的,添加课程(AddCourse)为例,画协作图:

     

     

           综上为交互的画图学习,还是我们要清楚,我们画图的原则,目标和方法即可。

     

    展开全文
  • 另外,还可以将较慢的处理逻辑、有并发数量限制的处理逻辑,通过消息队列放在后台处理,例如FLV视频转换、发送手机短信、发送电子邮件等。( 数据结构里最重要的两个结构就是树和图。比如一个公司由上到下的成员...
  • NSQ消息队列调研与使用

    千次阅读 2019-04-23 09:48:14
    消息队列解决的问题 模块间解耦:将一个流程加入一层数据接口拆分成两个部分,上游专注通知,下游专注处理,服务接口依赖变为数据依赖 消息的异步:上游发送消息以后可以马上返回,处理工作交给下游进行,最终一致...
  • RabbitMQ消息中间件技术精讲全集

    千次阅读 2019-09-10 12:35:41
    RabbitMQ消息中间件技术精讲 导航:RabbitMQ消息中间件技术精讲一. 主流消息中间件介绍1.1 ActiveMQ1.2 Kafka1.3 RocketMQ1.4 RabbitMQ二. RabbitMQ核心概念及AMQP协议 一. 主流消息中间件介绍 1.1 ActiveMQ ...
  • 目录 零、简介 一、MpAndroidChart的基本使用 ...3.举例 4.常用API 二、MpAndroidChart-LineChart的基本使用配置 1.XAxis(X轴) 2.YAxis(Y轴) 3.Legend(图例:即上图所示的曲线图下面的 温度) 4.Desc...
  • 一、基本概念1.1什么是库在windows平台和linux平台下都大量存在着库。本质上来说库是一种可执行代码的二进制... 1.2库的种类linux下的库有两种:静态库和共享库(动态库)。二者的不同点在于代码被载入的时刻不同。静
  • 动态主机配置协议 (DHCP)解析 DHCP 的工作方式 如何配置 IP 地址? 如何配置呢?若用命令行自己配置一个地址。可以使用ifconfig,也可以使用 ip addr。设置好了以后,用这两个命令,将网卡 up 一下,就可以开始...
  • 动态路由协议

    千次阅读 2021-12-12 20:38:43
    ISIS:(Intermediate System to)中间系统到中间系统 BGP:(Border Gateway Protocol)边界网关协议 不同路由协议是不能通信的,但可以使用其他方法让他们通信 举例 人与人的语言 动态路由特点 减少了管理任务:只...
  • Ubuntu中动态库.so和静态库.a介绍

    千次阅读 2019-03-12 15:48:14
    ( 今天在编译某个开源代码的时候遇到glog库的链接问题,由于对.so和.a动态库和静态库不够明白,于是学习了一波) 转自:http://www.cnblogs.com/laojie4321/archive/2012/03/28/2421056.html 在Linux中创建静态库...
  • 本案例是一个专注于flink动态规则计算的项目,核心技术组件涉及flink、hbase、clickhouse、drools等 项目可根据各类个性化需求进行二次开发后,直接用于实时运营,实时风控、交通监控等场景的线上生产 项目完整视频...
  • 动态更新DNS的区数据,告别手动修改配置文件的low操作,规避生产风险。
  • 优点:有消息就推给消费者。延迟小,几乎可以做到实时。等等。。。。 缺点:Server端接收到消息后,主动把消息推送给Client端,实时性高。对于一个提供队列服务的Server来说,用Push方式主动推送有很多弊端;首先是...
  • 用线程来举例的话,rt_thread_init对应静态定义方式,rt_thread_create对应动态定义方式。 使用静态定义方式时,必须先定义静态的线程控制块,并且定义好堆栈空间,然后调用rt_thread_init来完成线程的初始化工作。...
  • 逆向工程核心原理——消息钩取

    千次阅读 2021-12-07 12:07:44
    下面举例向各位 进一步说明“钩子”这一概念。 消息钩取 Windows操作系统向用户提供GUI (Graphic User Interface,图形用户界面),它以事件驱动 (Event Driven)方式工作。在操作系统中借助键盘、鼠标,选择菜单...
  • Flex与Java的简易消息推送机制举例-

    万次阅读 2011-12-19 15:51:30
    Flex与Java的简易消息推送机制举例 使用adobe提供的java flex通信包blazeDS中提供的推送功能实现Java到Flex的数据推送过程 1. 从adobe官网下载blazeds.war,这是一个压缩包,里面的包含META-INF和WEB-INF两个文件夹...
  • 一次性订阅消息 如有理解错误,请评论 讨论更正,一起学习。 一、个人理解: 1、订阅授权次数跟收消息次数的关系? ①、用户授权允许一次,才会收到一次(前提是用户接收通知打开,并且对应模板通知为接收状态)...
  • 什么是动态代理? 假如我有一个user对象,该对象里面有4个方法,增、删、改、查,外界能直接调用这4个方法吗?拿百度来说,你能随便对百度上的内容进行增、删、改、查操作吗?你最多能执行查的操作,增、删、改的...
  • 嵌入式实时操作系统12——信号,互斥,消息

    千次阅读 热门讨论 2022-01-15 12:23:13
    任务间信息交互的动态图: 1.任务间信息交互是什么 操作系统中每一个任务都是一个独立自主的小程序。任务的典型形式如下: 从形式上看任务可以在自己的资源空间中独立运行,事实上只有小部分任务完全独立于其它...
  • LTE网络有关系统消息(MIB/SIB)深度解析 参考链接:https://mp.weixin.qq.com/s?__biz=MzU1NjU5ODg3NQ==&mid=2247485713&idx=1&sn=5f3ccfa33f6ef9713a4246041d5499ae&chksm=fbc3da25ccb453337432d...
  • Windows 窗口发送消息参数详解

    万次阅读 2020-06-24 15:32:25
    窗口发送消息参数详解 // 窗口.发送消息 函数功能: 将指定的消息发送到一个窗口,同win32 api 里面的SendMessage等同的效果 中文函数原型: 发送消息(hwnd,msg,wparam,iparam) 英文函数原型: sendmessage(hwnd,...
  • 页面置换算法有哪些 介绍一下 5.2 Linux学习宝典 (1)《鸟哥的Linux私房菜》第四版 (2)LINUX宝典 第9版 (3)linux命令总结精简 第六个 网络相关 6.1 美团面试问题 OSI七层模型都是什么 举例网络协议,都在哪个层...
  • 服务消息驱动:Stream

    千次阅读 多人点赞 2021-02-06 01:48:49
    目录第一章 Stream介绍1.1、什么是Stream1.2、为啥用Stream第二章 Stream重要概念2.1、基本流程2.2、常用注解2.3、其他术语第三章 Stream入门案例3.1、项目准备与启动3.2、创建消息生产者3.3、创建消息消费者第四章 ...
  • Rt-Thread学习笔记-----消息队列(四)

    千次阅读 2020-04-30 22:00:38
    消息队列 消息队列是另一种常用的线程间通讯方式,是邮箱的扩展。可以应用在多种场合:线程间的消息交换、使用串口接收不定长数据等。 消息队列的工作机制 消息队列能够接收来自线程或中断服务例程中不固定长度的...
  • 在一个项目中需要调用外部接口,此接口一次只能处理8个请求,多于8个请求过来,nginx会为了保护接口直接踢回请求(返回500null错误),而在本项目中使用了消息队列机制,所以有可能会一次从消息队列中消费多条数据,...
  • 开源百万级分布式 MQTT 消息服务器EMQX 一. 是什么1. 简介2. 分类3. EMQ X 消息服务器功能列表二. 安装1. 安装方式rpm安装docker安装免安装2. 目录结构3. 使用websocket实现消息的收发三. 使用emqx基础功能1. ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 58,475
精华内容 23,390
关键字:

动态消息举例