本章内容
- 解耦思维
- 实例解析
- 实现RPC功能
在项目开发中,我们谈论最多的应该就是项目的可扩展性,解耦项目中的各模块就是解决扩展性的一种实现方式。为什么要解耦?什么样的场景需要解耦呢?
解耦思维
项目开发使用的技术就好像是一个项目的发动机一样,主要是为了业务提供充足的动力。随时间的推移,公司业务会不断的发生变化。就需要技术进行支持。所以就要求在项目的开发过程中,预留一定的扩展性,而要扩展就需要按某种规则拆分项目为小的模块,而模块与模块之间的耦合性越低,相互影响就越小,扩展时系统稳定性就越好。同时,也利于日常的维护。至于拆分的粒度也要充分讨论,如果拆分的太细,在之后需要开发新需求时就需要更多的组共同进行协作,沟通成本会很大。如果太粗又达不到解耦的目的。应用程序和存储之间直接耦合通常就是导致程序被淘汰的主要原因。这也正是MQ能够帮我们解决的问题所在。
面向消息的设计主要是考虑如何把一个比较耗时的程序移出,单独处理而使主程序继续它的工作。本质就是异步思维考虑解耦请求和具体的操作(与生产者和消息者思想相同),很像(AIO的做法)。这也比较符合现实世界生活,例如:我们去餐厅单餐时,前台接待我们后把订单交给后厨,之后又接收其它客户,而我们在点餐完成后也没有一直在那里等待而是玩着手机等待叫号。
面向消息的程序还有一个特点就是 你关心的是完成任务,但并不是实时完成的,无须应答请求。而当我们完成任务的动作无法跟上请求的速度时,我们还可以利用自动轮询的模式,把MQ充当负载均衡器来使用,使得程序有更好的扩展性。
发后即忘模型
匹配这种模型的几种一般类型的任务:
- 批处理,针对大型数据集合的工作。这种类型的工作一般可以构建为单一的任务请求,或者多个任务对数据集合的独立部分进行操作。
- 通知,对发生事情的描述。其内容可以是某些日志,或者是一个报告需要发送给另一个或多个程序。
- 并行处理,对相互独立的程序有统一的触发点。例如:客户下单后(触发点),给客户添加积分、通知仓库准备发货、通知财务开相应的开票等等 这些操作并没有明显的串行关系。
实例解析
实例一
星期天,你在家躺在沙发上嗑着瓜子、看着电视(也有可能是你老婆,哈哈)。中午到了,你命令你媳妇做饭,糖醋排骨,你媳妇就去忙活了。5分钟后你又想吃鸡了,于是又命令你媳妇再做一个土豆烧鸡。
特点:你自己就可以看到是一个生产者(命令的发出者),而你媳妇就是一个消费者(命令的接收执行者)。你与你媳妇之间只有一个队列通道,接收到命令后也是按顺序做的饭,所以可以使用direct类型的交换器。
首先,就是你媳妇接收你的命令
public void entertainYou(String need){ Connection connection = ConnectionPool.getConnection(); try { Channel channel = connection.createChannel(); channel.exchangeDeclare(Consts.EXCHANGE_NAME, BuiltinExchangeType.DIRECT,true); channel.queueDeclare(Consts.QUEUE_NAME,true,false,false,null); channel.queueBind(Consts.QUEUE_NAME,Consts.EXCHANGE_NAME,"command.make_lunch"); Map<String,String> content = new HashMap<String, String>(); content.put("id", UUID.randomUUID().toString()); content.put("need",need); content.put("time", Calculator.getCurrTime()); String str = new JSONWriter().write(content); channel.basicPublish(Consts.EXCHANGE_NAME,"command.make_lunch",getBasicProperties(), str.getBytes("UTF-8")); ConnectionPool.closeChannel(channel); ConnectionPool.closeConnection(connection); } catch (IOException e) { e.printStackTrace(); } } private AMQP.BasicProperties getBasicProperties(){ return basicProperties==null? basicProperties = new AMQP.BasicProperties.Builder() .contentType(Consts.ContentType.JSON) .contentEncoding("UTF-8") .build():basicProperties; }
然后去厨房做饭:
public void makeLunch(){ Connection connection = ConnectionPool.getConnection(); try { final Channel channel = connection.createChannel(); channel.exchangeDeclare(Consts.EXCHANGE_NAME, BuiltinExchangeType.DIRECT,true); channel.queueDeclare(Consts.QUEUE_NAME,true,false,false,null); channel.queueBind(Consts.QUEUE_NAME,Consts.EXCHANGE_NAME,"command.make_lunch"); channel.basicConsume(Consts.QUEUE_NAME,false,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String contentType = properties.getContentType(); //System.out.println(contentType); Map<String,String> content = (Map<String,String>)new JSONReader().read(new String(body)); System.out.println("你媳妇接受了你的请求:"+content); getChannel().basicAck(envelope.getDeliveryTag(),false); } }); } catch (IOException e) { e.printStackTrace(); } }
测试你的命令是否可用呢?
public static void main(String args[]){ //我发出命令 System.out.println("我命令媳妇去做饭!!"); cashier.entertainYou("我要吃糖醋排骨"); System.out.println("我继续