【Way to Java】面试题-猫老鼠主人

题目

猫大叫一声,所有的老鼠都开始逃跑,主人被惊醒?

观察者模式

在这里插入图片描述
简单来说就是目标含有一个容器,放置所有观察者,目标发生变化,通知观察者。

Observable类和Observer接口

java.util包内有这两个类用于实现观察周模式。

  • 观察者继承Observer接口,实现update方法,处理目标变化之后的响应
    在这里插入图片描述
  • Observable作为目标,含有容器obs存放观察者,提供add和delete方法
  • Observable发生变化时,遍历obs调用update方法来通知观察者
    在这里插入图片描述

发布订阅模式

在这里插入图片描述

  • 观察者模式中,目标需要知道观察者的存在,有直接调用关系;
  • 发布订阅模式中,发布者不需要知道订阅者的存在,没有直接联系;
  • 观察者模式中,目标和观察者必须同时在线,调用需要等待结果;
  • 发布订阅模式中,发布者和订阅者运行相互独立,互不干扰;

发布订阅模式和观察者模式有相似之处,但是一些地方有升级,是观察者模式的解耦升级版

Spring对发布订阅模式的支持

在这里插入图片描述

ApplicationContext

在这里插入图片描述

ApplicationEventPublisher

Spring容器的上下文接口继承了ApplicationEventPublisher接口,提供了两个方法:一个是容器级的事件、一个是业务的事件
在这里插入图片描述

ApplicationEvent

四个容器级的事件:容器关闭,容器刷新,容器启动,容器停止
在这里插入图片描述

ApplicationListener

实现这个接口,监听事件,实现onApplicationEvent方法处理时间,或者在类上面添加注解@EventListener
在这里插入图片描述

模拟业务场景

常规酒店预订逻辑

预定酒店 - 处理酒店预订业务 - 发送短信 - 发送邮件 - 预定酒店结束
在这里插入图片描述

	@Transactional
	public void addHotelOrder(HotelOrder order) {
		// 此处省略一万行代码,例如积分优惠券业务
		hotelOrderMapper.insert(order);
		// 发送短信
		OrderMsg msg = new OrderMsg(order.getId(), order.getPhone(), "Order success.", new Date());
		msgService.send(msg);
		// 发送邮件
		OrderEmail email = new OrderEmail("Order success.", order.getId());
		emailService.send(email);
	}

这种业务逻辑,第三方提供的短信和邮件的增值业务,绑架了预定酒店的主营业务,如果第三方业务不稳定会影响主营业务

发布订阅者模式改造

在这里插入图片描述
这样短信和邮件业务的故障就不会影响酒店预订的主营业务,代码如下:

	@Resource
	private ApplicationContext context;
	@Transactional
	public void addHotelOrder(HotelOrder order) {
		// 此处省略一万行代码,例如积分优惠券业务
		hotelOrderMapper.insert(order);
		// 事件广播
		context.publishEvent(order);
	}
	@EventListener
	public void handleOrderEvent(HotelOrder order) {
		// 发送短信
		OrderMsg msg = new OrderMsg(order.getId(), order.getPhone(), "Order success.", new Date());
		this.send(msg);
	}
	@EventListener
	public void handleOrderEvent(HotelOrder order) {
		// 发送邮件
		OrderEmail email = new OrderEmail("Order success.", order.getId());
		this.send(msg);
	}

然后需要开启异步事件处理,和配置线程池

	<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
		<!-- 注入任务执行器 这样就实现了异步调用(缺点是全局的,要么全部异步 要么全部同步,删除这个属性即为同步 -->
		<property name="taskExecutor" rer="coreTaskExecutor" />
	</bean>

	<bean id="coreTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolExecutor">
		<property name="corePoolSize" rer="10" />
		<property name="maxPoolSize" rer="20" />
		<property name="queueCapacity" rer="25" />
		<property name="threadNamePrefix" rer="CoreTaskExecutor" />
	</bean>

源码分析

this.getApplicationEventMulticaster() 获取广播器,由配置文件配置
在这里插入图片描述
SimpleApplicationEventMulticaster的multicastEvent方法
在这里插入图片描述
这就是为什么加了bean配置,就是异步执行,不加配置就是同步执行

分布式系统如何解耦

在这里插入图片描述
分布式系统直接调用的缺点:
1、不同系统的消息格式有区别,直接调用要匹配其他多个模块的消息格式,工作量大 强耦合
2、一个模块的调用失败会导致整体业务的回滚,分布式事务的回滚技术难度也非常大

基于消息中间件

分布式环境使用,基于JMS规范,支持两种消息传送模型:点对点消息通信模型和发布订阅模型,例如rabbitmq、kafka等

基于zookeeper的实现方案

基于zk的watch机制实现,面向复杂且容易出错的分布式一致性服务,比如分布式锁、配置中心、集群管理、服务注册中心等,例如如下zk配置中心结构
在这里插入图片描述

	public class ZookeeperConnector implements Runnable {
		private static Logger logger = LoggerFactory.getLogger(ZookeeperConnector.class);
		
		protected boolean ryan = true;
		private static final String CONF_PATH = "/configration";
		private static final String ZOOKEEPER_IP_PORT = "localhost:2181";
		private ZkClient client = new ZkClient(ZOOKEEPER_IP_PORT, 1000, 1000, new SerializableSerializer());
		
		private String dbIp;

		public void run() {
			dbIp = client.readData(CONF_PATH);
			logger.info(Thread.currentThread().getName() + "获得配置信息: " + dbIp);
			IZkDataListener listener = new IZkDataListener() {
				public void handleDataDeleted(String dataPath) throws Exception {
					logger.info("---------Node deleted.-----------")
				}
				public void handleDataChange(String dataPath, Object data) throws Exception {
					if(data==null) return;
					if(!dbIp.equals(data.toString())){
						dbIp = client.readData(CONF_PATH);
						logger.info(Thread.currentThread().getName() + ", New configuration:" + dbIp);
					}
				}
			};
			client.subscribeDataChanges(CONF_PATH, listener);
		}
	}

测试代码如下:
在这里插入图片描述

附:动态更新连接池

上面只是动态获取到配置参数,但是如何动态更新连接池?

PooledDataSource

这个类是mybatis数据库连接池
在这里插入图片描述

PoolState

其中PoolState是数据结构,包含空闲连接池和活跃连接池等
在这里插入图片描述

popConnection(String username, String password)

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值