下面我将为你详细讲解使用Zookeeper保存数据库的配置并实现动态刷新的实现过程。本文主要分为以下几个部分:
- MyBatis使用Zookeeper保存数据库的配置的原理
- 实现动态刷新的流程
- 代码实现及示例说明
1. MyBatis使用Zookeeper保存数据库的配置的原理
MyBatis使用Zookeeper保存数据库的配置,可以将配置信息保存在Zookeeper的某个节点上,MyBatis应用程序可以从该节点上获取配置信息。由于数据存储在Zookeeper上,因此可以实现动态刷新,直接修改Zookeeper上的配置,应用程序能够及时获取最新的配置信息。
以下是使用Zookeeper保存MyBatis数据库配置的具体原理:
- 在MyBatis配置文件中添加Zookeeper配置信息(如Zookeeper连接地址、节点路径等);
- 应用程序启动时,连接Zookeeper并获取指定节点上的配置信息;
- 将获取到的配置信息设置到MyBatis的SqlSessionFactoryBean中;
- 当Zookeeper上的配置信息发生变化时,应用程序能够及时获取最新的配置信息,并将其设置到SqlSessionFactoryBean中。
2. 实现动态刷新的流程
具体实现动态刷新的流程如下:
- 连接Zookeeper,获取指定节点上的配置信息;
- 构建MyBatis的SqlSessionFactoryBean时,将获取到的配置信息设置到该Bean中;
- 监听Zookeeper节点上配置信息的变化;
- 当Zookeeper上的配置信息发生变化时,动态更新SqlSessionFactoryBean中的配置信息。
3. 代码实现及示例说明
下面以一个实例来说明如何使用Zookeeper保存数据库的配置并实现动态刷新。
代码实现
在这个实现中,我们使用了Spring和Curator来连接Zookeeper。而这里使用了org.springframework.jdbc.datasource.DataSourceTransactionManager
来实现事务控制的方式,如有需要,可以查看源代码中详细的内容。
- 在pom.xml文件中添加以下依赖:
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Curator -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
- 在MyBatis配置文件中添加Zookeeper的配置信息:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/>
<property name="configLocation" value="classpath:/mybatis-config.xml"/>
<property name="configurationProperties">
<props>
<prop key="zookeeper.url">192.168.1.101:2181</prop>
<prop key="zookeeper.config.node">/config/mybatis</prop>
</props>
</property>
</bean>
- 实现Zookeeper的连接和配置信息的获取逻辑:
@Service
public class ZookeeperHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperHolder.class);
private CuratorFramework curatorFramework;
private String zookeeperConnectUrl;
private String zookeeperConfigNode;
private Properties props;
public ZookeeperHolder(//
@Value("${zookeeper.url}") String zookeeperConnectUrl,//
@Value("${zookeeper.config.node}") String zookeeperConfigNode) {
this.zookeeperConnectUrl = zookeeperConnectUrl;
this.zookeeperConfigNode = zookeeperConfigNode;
try {
curatorFramework = CuratorFrameworkFactory.newClient(zookeeperConnectUrl, new ExponentialBackoffRetry(1000, 3));
curatorFramework.start();
props = loadFromZookeeper();
} catch (Exception exception) {
LOGGER.error("Failed to get config from zookeeper, will continue with empty config", exception);
}
}
public Properties getProps() {
return props;
}
public void addChangeListener() throws Exception {
PathChildrenCache childrenCache = new PathChildrenCache(curatorFramework, zookeeperConfigNode, true);
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) {
switch (pathChildrenCacheEvent.getType()) {
case CHILD_ADDED:
case CHILD_REMOVED:
case CHILD_UPDATED:
try {
Properties newProps = loadFromZookeeper();
if (newProps != null && newProps.size() > 0) {
synchronized (ZookeeperHolder.this) {
ZookeeperHolder.this.props = newProps;
}
LOGGER.info("Update zookeeper props: {}", newProps);
} else {
LOGGER.warn("No found properties at zookeeper node: {}", zookeeperConfigNode);
}
} catch (Exception exception) {
LOGGER.error("Failed to update config from zookeeper", exception);
}
break;
default:
LOGGER.warn("Unknown event type: {}", pathChildrenCacheEvent.getType());
break;
}
}
});
childrenCache.start();
}
private Properties loadFromZookeeper() throws Exception {
if (curatorFramework.checkExists().forPath(zookeeperConfigNode) == null) {
LOGGER.warn("Node not exists in zookeeper node: {}", zookeeperConfigNode);
return null;
}
Properties props = new Properties();
List<String> children = curatorFramework.getChildren().forPath(zookeeperConfigNode);
for (String child : children) {
byte[] bytes = curatorFramework.getData().forPath(zookeeperConfigNode + "/" + child);
if (bytes == null) {
LOGGER.warn("Not found data for child: {}", child);
continue;
}
String value = new String(bytes, "UTF-8");
props.put(child, value);
}
return props;
}
}
- 在MyBatis的Mapper和Service中,通过@Autowired注入ZookeeperHolder,然后将获取到的配置信息设置到SqlSessionFactoryBean中即可:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private ZookeeperHolder zookeeperHolder;
@Override
public User queryUserById(Long id) {
DataSource dataSource = new DruidDataSource();
JdbcTransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("Development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.addMapper(UserMapper.class);
Properties properties = zookeeperHolder.getProps();
if (properties != null) {
Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
for (Map.Entry<Object, Object> entry : entrySet) {
configuration.set(entry.getKey().toString(), entry.getValue().toString());
}
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
return userMapper.queryUserById(id);
} finally {
session.close();
}
}
}
- 将ZookeeperHolder添加到Spring的Bean容器中,然后在Spring的创建和启动过程中添加监听器:
@Configuration
public class MyConfig implements WebApplicationInitializer {
@Bean
public ZookeeperHolder zookeeperHolder() {
return new ZookeeperHolder("192.168.1.101:2181", "/config/mybatis");
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(MyConfig.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
try {
rootContext.getBean(ZookeeperHolder.class).addChangeListener();
} catch (Exception exception) {
LOGGER.error("Failed to add zookeeper change listener", exception);
}
}
}
示例说明
在这个实现中,我们使用了一个名为/config/mybatis
的Zookeeper节点来存储MyBatis的配置信息,包括JDBC的URL、用户名、密码等信息。
下面是一个简单的MyBatis Mapper和Service用于查询用户信息并返回。这些示例代码都是基于上面介绍的代码实现中编写的:
@Mapper
@Repository
public interface UserMapper {
User queryUserById(@Param("id") Long id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserById(Long id) {
return userMapper.queryUserById(id);
}
}
最后,我们假设我们需要修改数据库的密码。我们不需要重启整个应用程序,只需要将密码更新到Zookeeper中即可。这样,MyBatis应用程序会及时从Zookeeper获取最新的密码。
以上就是使用Zookeeper保存数据库的配置并实现动态刷新的实现过程。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:MyBatis使用Zookeeper保存数据库的配置可动态刷新的实现代码 - Python技术站