当前位置: 首页 > 后端技术 > Java

SpringBoot做到了读写分离,还有谁做不到??

时间:2023-04-01 20:17:32 Java

来源:www.liaoxuefeng.com第一步:配置多数据源SpringBoot基础知识就不介绍了。推荐这个实用教程:https://github.com/javastacks...首先,我们在SpringBoot中配置两个数据源,第二个数据源是ro-datasource:spring:datasource:jdbc-url:jdbc:mysql://localhost/test用户名:rw密码:rw_password驱动程序类名:com.mysql。jdbc.Driverhikari:pool-name:HikariCPauto-commit:false...ro-datasource:jdbc-url:jdbc:mysql://localhost/test用户名:ro密码:ro_password驱动程序类名:com.mysql.jdbc.Driverhikari:pool-name:HikariCPauto-commit:false...在开发环境下,不需要配置主从数据库。只需要为数据库设置两个用户,一个rw有读写权限,另一个ro只有SELECT权限,模拟了生产环境中主从数据库的读写分离。在SpringBoot的配置代码中,我们初始化了两个数据源:@SpringBootApplicationpublicclassMySpringBootApplication{/***主数据源。*/@Bean("masterDataSource")@ConfigurationProperties(prefix="spring.datasource")DataSourcemasterDataSource(){logger.info("创建主数据源...");返回DataSourceBuilder.create().build();}/***从属(只读)数据源。*/@Bean("slaveDataSource")@ConfigurationProperties(prefix="spring.ro-datasource")DataSourceslaveDataSource(){logger.info("createslavedatasource...");返回DataSourceBuilder.create().build();}...}第二步:编写RoutingDataSource然后,我们使用Spring内置的RoutingDataSource将两个真实的数据源代理成一个动态数据源:}}对于这个RoutingDataSource,需要在SpringBoot中配置并设置为主数据源:@SpringBootApplicationpublicclassMySpringBootApplication{@Bean@PrimaryDataSourceprimaryDataSource(@Autowired@Qualifier("masterDataSource")DataSourcemasterDataSource,@Autowired@Qualifier("slaveDataSource")DataSourceslaveDataSource){logger.info("创建路由数据源...");Mapmap=newHashMap<>();map.put("masterDataSource",masterDataSource);map.put("slaveDataSource",slaveDataSource);RoutingDataSourcerouting=newRoutingDataSource();routing.setTargetDataSources(地图);routing.setDefaultTargetDataSource(masterDataSource);返回路由;}...}现在,RoutingDataSource配置好了,但是路由选择是硬编码的,即总是返回“masterDataSource”,现在问题来了:动态选择key如何存储,设置key在哪里?在Servlet线程模型中,使用ThreadLocal来存储key是最合适的,所以我们写了一个RoutingDataSourceContext来设置和动态存储key:publicclassRoutingDataSourceContextimplementsAutoCloseable{//holdsdatasourcek本地线程中的ey:staticfinalThreadLocalthreadLocalDataSourceKey=newThreadLocal<>();publicstaticStringgetDataSourceRoutingKey(){Stringkey=threadLocalDataSourceKey.get();返回键==null?“masterDataSource”:钥匙;}publicRoutingDataSourceContext(Stringkey){threadLocalDataSourceKey.set(key);}publicvoidclose(){threadLocalDataSourceKey.remove();}}然后,修改RoutingDataSource,获取关键代码如下:}}这样,在某个地方,比如Controller方法内部,就可以动态设置DataSource的Key:@ControllerpublicclassMyController{@Get("/")publicStringindex(){Stringkey="奴隶数据源";try(RoutingDataSourceContextctx=newRoutingDataSourceContext(key)){//TODO:return"html...www.liaoxuefeng.com";}}}至此,我们就成功实现了动态路由访问数据库。这种方法是可行的,但是如果需要从数据库中读取,需要加一大段try(RoutingDataSourceContextctx=...){}的代码,使用起来很不方便。有没有办法简化这个?有!我们仔细想想,Spring提供的声明式事务管理只需要在一个Java方法上加上一个@Transactional()注解,这个方法就自动有了一个事务。我们也可以写一个类似的@RoutingWith("slaveDataSource")注解,放在一个Controller方法上,在这个方法内部会自动选择对应的数据源。代码应该是这样的:@ControllerpublicclassMyController{@Get("/")@RoutingWith("slaveDataSource")publicStringindex(){return"html...www.liaoxuefeng.com";}}这样,完全不需要修改应用程序的逻辑,只需要在需要的地方加上注解,就可以自动实现动态数据源切换,是最简单的方法。如果我们想在应用程序中少写一些代码,我们就得在底层多做一点工作:我们必须使用类似于Spring的机制来实现声明式事务,即使用AOP来实现动态数据源切换。实现这个功能也很简单。写一个RoutingAspect,用AspectJ实现一个Around拦截:@Aspect@ComponentpublicclassRoutingAspect{@Around("@annotation(routingWith)")publicObjectroutingWithDataSource(ProceedingJoinPointjoinPoint,RoutingWithroutingWith)throwsThrowable{Stringkey=routingWith.value();尝试(RoutingDataSourceContextctx=newRoutingDataSourceContext(key)){returnjoinPoint.proceed();注意,该方法的第二个参数RoutingWith是Spring传入的注解实例。value()获取配置的键。在编译之前,需要添加一个Maven依赖:使用Annotate动态选择数据源的能力。最后的重构步骤是用字符串常量替换散落在各处的“masterDataSource”和“slaveDataSource”。使用限制受Servlet线程模型的限制。动态数据源在请求内设置后不可修改,即@RoutingWith不可嵌套。另外,当@RoutingWith和@Transactional混合使用时,需要设置AOP的优先级。本文代码需要SpringBoot支持,JDK1.8编译和open-parameters编译参数。近期热点文章推荐:1.1000+Java面试题及答案(2021最新版)2.厉害了!Java协程来了。..3.玩大!Log4j2.x再次爆发。..4、SpringBoot2.6正式发布,一大波新特性。.5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!