Zookeeper系列四:Zookeeper實現分布式鎖、Zookeeper實現配置中心

一、Zookeeper實現分布式鎖

分布式鎖主要用于在分布式環境中保證數據的一致性。

包括跨進程、跨機器、跨網絡導致共享資源不一致的問題。

1. 分布式鎖的實現思路

說明:

這種實現會有一個缺點,即當有很多進程在等待鎖的時候,在釋放鎖的時候會有很多進程就過來爭奪鎖,這種現象稱為?“驚群效應”

2.?分布式鎖優化后的實現思路

?

3. Zookeeper分布式鎖的代碼實現

準備工作:

1)安裝Zookeeper,具體參考我前面的我文章Zookeeper系列一:Zookeeper介紹、Zookeeper安裝配置、ZK Shell的使用

2)新建一個maven項目ZK-Demo,然后在pom.xml里面引入相關的依賴

        <dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.10</version></dependency>

3.1?Zookeeper分布式鎖的核心代碼實現

實現邏輯參考“2.?分布式鎖優化后的實現思路”中的流程圖

package com.study.demo.lock;import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/**
* 
* @Description: Zookeeper分布式鎖的核心代碼實現
* @author leeSmall
* @date 2018年9月4日
*
*/
public class DistributedLock implements Lock {private static Logger logger = LoggerFactory.getLogger(DistributedLock.class);private static final String ZOOKEEPER_IP_PORT = "192.168.152.130:2181";private static final String LOCK_PATH = "/LOCK";private ZkClient client = new ZkClient(ZOOKEEPER_IP_PORT, 4000, 4000, new SerializableSerializer());private CountDownLatch cdl;private String beforePath;// 當前請求的節點前一個節點private String currentPath;// 當前請求的節點// 判斷有沒有LOCK目錄,沒有則創建public DistributedLock() {if (!this.client.exists(LOCK_PATH)) {this.client.createPersistent(LOCK_PATH);}}public void lock() {//嘗試去獲取分布式鎖失敗if (!tryLock()) {//對次小節點進行監聽
            waitForLock();lock();} else {logger.info(Thread.currentThread().getName() + " 獲得分布式鎖!");}}public boolean tryLock() {// 如果currentPath為空則為第一次嘗試加鎖,第一次加鎖賦值currentPathif (currentPath == null || currentPath.length() <= 0) {// 創建一個臨時順序節點currentPath = this.client.createEphemeralSequential(LOCK_PATH + '/', "lock");System.out.println("---------------------------->" + currentPath);}// 獲取所有臨時節點并排序,臨時節點名稱為自增長的字符串如:0000000400List<String> childrens = this.client.getChildren(LOCK_PATH);//由小到大排序所有子節點
        Collections.sort(childrens);//判斷創建的子節點/LOCK/Node-n是否最小,即currentPath,如果當前節點等于childrens中的最小的一個就占用鎖if (currentPath.equals(LOCK_PATH + '/' + childrens.get(0))) {return true;} //找出比創建的臨時順序節子節點/LOCK/Node-n次小的節點,并賦值給beforePathelse {int wz = Collections.binarySearch(childrens, currentPath.substring(6));beforePath = LOCK_PATH + '/' + childrens.get(wz - 1);}return false;}//等待鎖,對次小節點進行監聽private void waitForLock() {IZkDataListener listener = new IZkDataListener() {public void handleDataDeleted(String dataPath) throws Exception {logger.info(Thread.currentThread().getName() + ":捕獲到DataDelete事件!---------------------------");if (cdl != null) {cdl.countDown();}}public void handleDataChange(String dataPath, Object data) throws Exception {}};// 對次小節點進行監聽,即beforePath-給排在前面的的節點增加數據刪除的watcherthis.client.subscribeDataChanges(beforePath, listener);if (this.client.exists(beforePath)) {cdl = new CountDownLatch(1);try {cdl.await();} catch (InterruptedException e) {e.printStackTrace();}}this.client.unsubscribeDataChanges(beforePath, listener);}//完成業務邏輯以后釋放鎖public void unlock() {// 刪除當前臨時節點
        client.delete(currentPath);}// ==========================================public void lockInterruptibly() throws InterruptedException {}public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false;}public Condition newCondition() {return null;}
}

?3.2?在業務里面使用分布式鎖

package com.study.demo.lock;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/**
* 
* @Description: 在業務里面使用分布式鎖
* @author leeSmall
* @date 2018年9月4日
*
*/
public class OrderServiceImpl implements Runnable {private static OrderCodeGenerator ong = new OrderCodeGenerator();private Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);// 同時并發的線程數private static final int NUM = 10;// 按照線程數初始化倒計數器,倒計數器private static CountDownLatch cdl = new CountDownLatch(NUM);private Lock lock = new DistributedLock();// 創建訂單接口public void createOrder() {String orderCode = null;//準備獲取鎖
        lock.lock();try {// 獲取訂單編號orderCode = ong.getOrderCode();} catch (Exception e) {// TODO: handle exception} finally {//完成業務邏輯以后釋放鎖
            lock.unlock();}// ……業務代碼
logger.info("insert into DB使用id:=======================>" + orderCode);}public void run() {try {// 等待其他線程初始化
            cdl.await();} catch (InterruptedException e) {// TODO Auto-generated catch block
            e.printStackTrace();}// 創建訂單
        createOrder();}public static void main(String[] args) {for (int i = 1; i <= NUM; i++) {// 按照線程數迭代實例化線程new Thread(new OrderServiceImpl()).start();// 創建一個線程,倒計數器減1
            cdl.countDown();}}
}

工具類:

package com.study.demo.lock;import java.text.SimpleDateFormat;
import java.util.Date;public class OrderCodeGenerator {// 自增長序列private static int i = 0;// 按照“年-月-日-小時-分鐘-秒-自增長序列”的規則生成訂單編號public String getOrderCode() {Date now = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");return sdf.format(now) + ++i;}}

二、Zookeeper實現配置中心

?1. 首先在zookeeper里面創建一個Jdbc的節點,在下面分別創建4個子節點/Jdbc/url、/Jdbc/uname、/Jdbc/password、/Jdbc/driver

create /Jdbc ''
create /Jdbc/url jdbc.mysql://192.168.152.1/dbspread 
create /Jdbc/uname root
create /Jdbc/password 123456
create /Jdbc/driver com.mysql.jdbc.Driver

注意:/Jdbc/url這個節點的值是錯的?

?

?

?2. 新建一個zkdemo的maven的web項目

項目結構如下:

2.1 在pom.xml文件里面引入下面依賴:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.study.demo</groupId><artifactId>zkdemo</artifactId><packaging>war</packaging><version>0.0.1-SNAPSHOT</version><name>zkdemo Maven Webapp</name><url>http://maven.apache.org</url><properties><spring.version>4.3.8.RELEASE</spring.version></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.10</version></dependency><dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.10</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.0.0</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-catalina</artifactId><version>7.0.39</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>2.7.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.41</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.25</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.9.1</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.1</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency></dependencies><build><finalName>zkdemo</finalName></build>
</project>

2.2 新建一個zookeeper配置中心類,從zookeeper動態獲取數據庫配置

package com.study.demo.config;import java.util.List;
import java.util.Properties;import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;import com.zaxxer.hikari.HikariDataSource;/**
* 
* @Description: zookeeper配置中心類,從zookeeper動態獲取數據庫配置
* @author leeSmall
* @date 2018年9月10日
*
*/
public class ZookeeperConfigurerCentral {//curator客戶端private CuratorFramework zkClient;//curator事件監聽private TreeCache treeCache;//zookeeper的ip和端口private String zkServers;//zookeeper上的/Jdbc路徑private String zkPath;//超時設置private int sessionTimeout;//讀取zookeeper上的數據庫配置文件放到這里private Properties props;public ZookeeperConfigurerCentral(String zkServers, String zkPath, int sessionTimeout) {this.zkServers = zkServers;this.zkPath = zkPath;this.sessionTimeout = sessionTimeout;this.props = new Properties();//初始化curator客戶端
        initZkClient();//從zookeeper的Jdbc節點下獲取數據庫配置存入props
        getConfigData();//對zookeeper上的數據庫配置文件所在節點進行監聽,如果有改變就動態刷新props
        addZkListener();}//初始化curator客戶端private void initZkClient() {zkClient = CuratorFrameworkFactory.builder().connectString(zkServers).sessionTimeoutMs(sessionTimeout).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();zkClient.start();}//從zookeeper的Jdbc節點下獲取數據庫配置存入propsprivate void getConfigData() {try {List<String> list = zkClient.getChildren().forPath(zkPath);for (String key : list) {String value = new String(zkClient.getData().forPath(zkPath + "/" + key));if (value != null && value.length() > 0) {props.put(key, value);}}} catch (Exception e) {e.printStackTrace();}}//對zookeeper上的數據庫配置文件所在節點進行監聽,如果有改變就動態刷新propsprivate void addZkListener() {TreeCacheListener listener = new TreeCacheListener() {public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {if (event.getType() == TreeCacheEvent.Type.NODE_UPDATED) {getConfigData();WebApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();HikariDataSource dataSource = (HikariDataSource) ctx.getBean("dataSource");System.out.println("================"+props.getProperty("url"));dataSource.setJdbcUrl(props.getProperty("url"));dataSource.setUsername(props.getProperty("uname"));dataSource.setPassword(props.getProperty("password "));dataSource.setDriverClassName(props.getProperty("driver "));}}};treeCache = new TreeCache(zkClient, zkPath);try {treeCache.start();treeCache.getListenable().addListener(listener);} catch (Exception e) {e.printStackTrace();}}public Properties getProps() {return props;}public void setZkServers(String zkServers) {this.zkServers = zkServers;}public void setZkPath(String zkPath) {this.zkPath = zkPath;}public void setSessionTimeout(int sessionTimeout) {this.sessionTimeout = sessionTimeout;}
}

2.3 新建一個加載props里面的數據庫配置的類

package com.study.demo.config;import java.util.Properties;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;/**
* 
* @Description: 加載props里面的數據庫配置,這個類等價于以前在xml文件里面的配置:
* <context:property-placeholder location="classpath:config/jdbc_conf.properties"/>
* @author leeSmall
* @date 2018年9月10日
*
*/
public class ZookeeperPlaceholderConfigurer extends PropertyPlaceholderConfigurer {private ZookeeperConfigurerCentral zkConfigurerCentral;@Overrideprotected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)throws BeansException {System.out.println(zkConfigurerCentral.getProps());super.processProperties(beanFactoryToProcess, zkConfigurerCentral.getProps());}public void setzkConfigurerCentral(ZookeeperConfigurerCentral zkConfigurerCentral) {this.zkConfigurerCentral = zkConfigurerCentral;}
}

2.4 在/zkdemo/src/main/webapp/WEB-INF/config/applicationContext.xml配置2.2和2.3新建的兩個主類

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config /><context:component-scan base-package="com.study.demo" /><!--通過構造函數注入zkServers、sessionTimeout、zkPath從zookeeper動態獲取數據庫配置  --><bean id="zkConfigurerCentral" class="com.study.demo.config.ZookeeperConfigurerCentral"><constructor-arg name="zkServers" value="192.168.152.130:2181" /><constructor-arg name="sessionTimeout" value="1000" /><constructor-arg name="zkPath" value="/Jdbc" /></bean><!--這個類等價于以前在xml文件里面的配置:<context:property-placeholder location="classpath:config/jdbc_conf.properties"/>  加載props里面的數據庫配置--><bean id="zkPlaceholderConfigurer" class="com.study.demo.config.ZookeeperPlaceholderConfigurer"><property name="zkConfigurerCentral" ref="zkConfigurerCentral" /><property name="ignoreUnresolvablePlaceholders" value="true" /><property name="order" value="1" /></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource"><ref bean="dataSource" /></property></bean><bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"destroy-method="shutdown"><property name="driverClassName" value="${driver}" /><property name="jdbcUrl" value="${url}" /><property name="username" value="${uname}" /><property name="password" value="${password}" /><!-- 連接只讀數據庫時配置為true, 保證安全 --><property name="readOnly" value="false" /><!-- 等待連接池分配連接的最大時長(毫秒),超過這個時長還沒可用的連接則發生SQLException, 缺省:30秒 --><property name="connectionTimeout" value="30000" /><!-- 一個連接idle狀態的最大時長(毫秒),超時則被釋放(retired),缺省:10分鐘 --><property name="idleTimeout" value="600000" /><!-- 一個連接的生命時長(毫秒),超時而且沒被使用則被釋放(retired),缺省:30分鐘,建議設置比數據庫超時時長少30秒,參考MySQL wait_timeout參數(show variables like '%timeout%';) --><property name="maxLifetime" value="1800000" /><!-- 連接池中允許的最大連接數。缺省值:10;推薦的公式:((core_count * 2) + effective_spindle_count) --><property name="maximumPoolSize" value="15" /></bean>
</beans>

2.5 在com.study.demo.controller新建測試類

測試類1:

package com.study.demo.controller;import java.io.Serializable;public class OrderModel implements Serializable {private static final long serialVersionUID = 1L;private int orderId;private int brandId;public int getOrderId() {return orderId;}public void setOrderId(int orderId) {this.orderId = orderId;}public int getBrandId() {return brandId;}public void setBrandId(int brandId) {this.brandId = brandId;}}
View Code

測試類2:

package com.study.demo.controller;import java.sql.ResultSet;
import java.sql.SQLException;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;@Repository
public class OrderDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public OrderModel findById() {String sql = "select * from tbl_order where order_id = 1";return jdbcTemplate.queryForObject(sql, new RowMapper<OrderModel>() {public OrderModel mapRow(ResultSet rs, int rowNum) throws SQLException {OrderModel payment = new OrderModel();payment.setOrderId(rs.getInt("order_id"));payment.setBrandId(rs.getInt("brand_id"));return payment;}});}
}
View Code

測試類3:

package com.study.demo.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class OrderService {@Autowiredprivate OrderDao dao;public OrderModel getById() {return dao.findById();}
}
View Code

測試類4:

package com.study.demo.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class OrderController {@Autowiredprivate OrderService service;@ResponseBody@RequestMapping(value = "/test", method = RequestMethod.GET)public String test() {OrderModel p = service.getById();return p.getBrandId() + "";}
}
View Code

2.6 其他附加配置和數據庫腳本

/zkdemo/src/main/webapp/WEB-INF/config/log4j.properties

log4j.rootLogger=INFO,console
log4j.logger.org.apache.zookeeper=DEBUG
log4j.logger.org.apache.curator=DEBUG
log4j.logger.java.lang.Exception=INFOlog4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{MM-dd HH:mm:ss.SSS} [%c:%p] %m%n
View Code

/zkdemo/src/main/webapp/WEB-INF/config/spring-mvc.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><mvc:default-servlet-handler /><mvc:annotation-drivencontent-negotiation-manager="contentNegotiationManager" /><context:component-scan base-package="com.study.demo"><context:include-filter type="annotation"expression="org.springframework.stereotype.Controller" /><context:exclude-filter type="annotation"expression="org.springframework.stereotype.Service" /></context:component-scan><bean id="stringHttpMessageConverter"class="org.springframework.http.converter.StringHttpMessageConverter"><property name="supportedMediaTypes"><list><bean class="org.springframework.http.MediaType"><constructor-arg index="0" value="text" /><constructor-arg index="1" value="plain" /><constructor-arg index="2" value="UTF-8" /></bean></list></property></bean><bean id="mappingJacksonHttpMessageConverter"class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /><beanclass="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"><property name="messageConverters"><list><ref bean="stringHttpMessageConverter" /><ref bean="mappingJacksonHttpMessageConverter" /></list></property></bean><bean id="contentNegotiationManager"class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"><property name="mediaTypes"><map><entry key="html" value="text/html" /><entry key="pdf" value="application/pdf" /><entry key="xsl" value="application/vnd.ms-excel" /><entry key="xml" value="application/xml" /><entry key="json" value="application/json" /></map></property><property name="defaultContentType" value="text/html" /></bean><bean id="viewResolver"class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><property name="order" value="0" /><property name="contentNegotiationManager" ref="contentNegotiationManager" /><property name="viewResolvers"><list><bean class="org.springframework.web.servlet.view.BeanNameViewResolver" /><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="viewClass"value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/WEB-INF/pages/" /><property name="suffix" value=".jsp"></property></bean></list></property><property name="defaultViews"><list><beanclass="org.springframework.web.servlet.view.json.MappingJackson2JsonView"><property name="extractValueFromSingleKeyModel" value="true" /></bean></list></property></bean>
</beans>
View Code

/zkdemo/src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><display-name>zkdemo</display-name><description>Zookeeper Demo Application</description><!--============================================================== --><!-- Context parameters definition --><!--============================================================== --><context-param><param-name>webAppRootKey</param-name><param-value>zkdemo.root</param-value></context-param><context-param><param-name>log4jConfigLocation</param-name><param-value>/WEB-INF/config/log4j.properties</param-value></context-param><context-param><param-name>log4jRefreshInterval</param-name><param-value>60000</param-value></context-param><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/applicationContext.xml</param-value></context-param><servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>spring</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!--============================================================== --><!-- Listener definition --><!--============================================================== --><listener><listener-class>org.springframework.web.util.Log4jConfigListener</listener-class></listener><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!--============================================================== --><!-- Filter definition --><!--============================================================== --><filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--============================================================== --><!-- Web Session definition --><!--============================================================== --><session-config><session-timeout>20</session-timeout></session-config><!--============================================================== --><!-- Redirect page definition --><!--============================================================== --><error-page><error-code>403</error-code><location>/403.jsp</location></error-page><error-page><error-code>404</error-code><location>/404.jsp</location></error-page><error-page><error-code>500</error-code><location>/500.jsp</location></error-page><!--============================================================== --><!-- First page definition --><!--============================================================== --><welcome-file-list><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>
View Code

數據庫腳本:

CREATE TABLE `tbl_order` (`order_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '訂單id',`brand_id` int(11) DEFAULT NULL COMMENT '品牌id',PRIMARY KEY (`order_id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='訂單表';INSERT INTO tbl_order VALUES('1','1')

?2.7 啟動項目在瀏覽器輸入地址http://localhost:8080/zkdemo/test查看效果

?

可以看到報錯了,這是因為我們之前設置了錯誤的url

create /Jdbc/url jdbc.mysql://192.168.152.1/dbspread 

修改url為正確的?

set /Jdbc/url jdbc:mysql://192.168.152.1:3306/dbspread

?

?再次輸入地址訪問查看效果:

http://localhost:8080/zkdemo/test

可以看到在沒有重啟服務的情況下,可以正常訪問獲取到值了,這是因為zookeeper的數據庫的配置動態刷新到服務了!

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/389722.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389722.shtml
英文地址,請注明出處:http://en.pswp.cn/news/389722.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

resize 按鈕不會被偽元素遮蓋

textarea默認有個resize樣式&#xff0c;效果就是下面這樣 讀 《css 揭秘》時發現兩個亮點&#xff1a; 其實這個屬性不僅適用于 textarea 元素&#xff0c;適用于下面所有元素&#xff1a;elements with overflow other than visible, and optionally replaced elements repre…

平臺api對數據收集的影響_收集您的數據不是那么怪異的api

平臺api對數據收集的影響A data analytics cycle starts with gathering and extraction. I hope my previous blog gave an idea about how data from common file formats are gathered using python. In this blog, I’ll focus on extracting the data from files that are…

709. 轉換成小寫字母

709. 轉換成小寫字母 給你一個字符串 s &#xff0c;將該字符串中的大寫字母轉換成相同的小寫字母&#xff0c;返回新的字符串。 示例 1&#xff1a;輸入&#xff1a;s "Hello" 輸出&#xff1a;"hello"示例 2&#xff1a;輸入&#xff1a;s "here…

前端技術周刊 2018-09-10:Redux Mobx

前端快爆 在 Chrome 10 周年之際&#xff0c;正式發布 69 版本&#xff0c;整體 UI 重新設計&#xff0c;同時iOS 版本重新將工具欄放置在了底部。API 層面&#xff0c;支持了 CSS Scroll Snap、前端資源鎖 Web Lock API、WebWorker 里面可以跑的 OffscreenCanvas API、toggleA…

PPT制作

0.【整體風格】整體風格統一 界面排版 0.1 字體大小&#xff1b; 0.2 字體顏色&#xff1b; 0.3 字體的種類統一(不是指只取一種字體)&#xff09; 1.【表達】結構化表達&#xff1b; 2.【取色】取色風格統一&#xff1b; 技巧&#xff1a;主色不超過三種&#xff0c;色彩不宜多…

1984. 學生分數的最小差值

1984. 學生分數的最小差值 給你一個 下標從 0 開始 的整數數組 nums &#xff0c;其中 nums[i] 表示第 i 名學生的分數。另給你一個整數 k 。 從數組中選出任意 k 名學生的分數&#xff0c;使這 k 個分數間 最高分 和 最低分 的 差值 達到 最小化 。 返回可能的 最小差值 。…

WBLoadingIndicatorView(加載等待動畫)

中文說明 基于CALayer封裝加載等待動畫&#xff0c;目前支持6種類型動畫&#xff1a; typedef NS_ENUM(NSInteger, WBLoadingAnimationType) { WBLoadingAnimationcircleStrokeSpinType, WBWBLoadingAnimationBallPulseType, WBWBLoadingAnimationBallClipRotateType, WBWBLoad…

邏輯回歸 概率回歸_概率規劃的多邏輯回歸

邏輯回歸 概率回歸There is an interesting dichotomy in the world of data science between machine learning practitioners (increasingly synonymous with deep learning practitioners), and classical statisticians (both Frequentists and Bayesians). There is gener…

sys.modules[__name__]的一個實例

關于sys.modules[__name__]的用法&#xff0c;百度上閱讀量比較多得一個帖子是&#xff1a;https://www.cnblogs.com/robinunix/p/8523601.html 對于里面提到的基礎性的知識點這里就不再重復了&#xff0c;大家看原貼就好。這里為大家提供一個詳細的例子&#xff0c;幫助大家更…

ajax不利于seo_利于探索移動選項的界面

ajax不利于seoLately, my parents will often bring up in conversation their desire to move away from their California home and find a new place to settle down for retirement. Typically they will cite factors that they perceive as having altered the essence o…

C#調用WebKit內核

原文:C#調用WebKit內核版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 https://blog.csdn.net/u013564470/article/details/80255954 系統要求 Windows與.NET框架 由于WebKit庫和.NET框架的要求&#xff0c;WebKit .NET只能在Windows系統上運行。從…

數據分析入門:如何訓練數據分析思維?

本文由 網易云 發布。 作者&#xff1a;吳彬彬&#xff08;本篇文章僅限知乎內部分享&#xff0c;如需轉載&#xff0c;請取得作者同意授權。&#xff09; 我們在生活中&#xff0c;會經常聽說兩種推理模式&#xff0c;一種是歸納 一種是演繹&#xff0c;這兩種思維模式能夠幫…

2011. 執行操作后的變量值

2011. 執行操作后的變量值 存在一種僅支持 4 種操作和 1 個變量 X 的編程語言&#xff1a; X 和 X 使變量 X 的值 加 1 –X 和 X-- 使變量 X 的值 減 1 最初&#xff0c;X 的值是 0 給你一個字符串數組 operations &#xff0c;這是由操作組成的一個列表&#xff0c;返回執行…

crontab的坑

使用crontab的話&#xff0c;任何命令都需要采用絕對路徑&#xff01;&#xff01;包括輸出文件位置 如&#xff1a;nohup /usr/sbin/tcpdump -i flannel.1 -nn -q -n tcp > /home/linjj/conns.log & 轉載于:https://www.cnblogs.com/linjj/p/9006419.html

559. N 叉樹的最大深度

559. N 叉樹的最大深度 給定一個 N 叉樹&#xff0c;找到其最大深度。 最大深度是指從根節點到最遠葉子節點的最長路徑上的節點總數。 N 叉樹輸入按層序遍歷序列化表示&#xff0c;每組子節點由空值分隔&#xff08;請參見示例&#xff09;。 示例 1&#xff1a; 輸入&#…

python Tags 母板 組件 靜態文件相關 自定義simpletag inclusion_tag

一.Tags(一)for 1.基本用法 <ul> {% for user in user_list %} <li>{{ user.name }}</li> {% endfor %} </ul> 2.for循環可用的一些參數 forloop.counter 當前循環的索引值&#xff08;從1開始&#xff09; …

el表達式取值優先級

不同容器中存在同名值時&#xff0c;從作用范圍小到大的順序依次嘗試取值&#xff1a;pageContext->request->session->application 轉載于:https://www.cnblogs.com/wrencai/p/9006880.html

數據探索性分析_探索性數據分析

數據探索性分析When we hear about Data science or Analytics , the first thing that comes to our mind is Modelling , Tuning etc. . But one of the most important and primary steps before all of these is Exploratory Data Analysis or EDA.當我們聽到有關數據科學或…

5930. 兩棟顏色不同且距離最遠的房子

5930. 兩棟顏色不同且距離最遠的房子 街上有 n 棟房子整齊地排成一列&#xff0c;每棟房子都粉刷上了漂亮的顏色。給你一個下標從 0 開始且長度為 n 的整數數組 colors &#xff0c;其中 colors[i] 表示第 i 棟房子的顏色。 返回 兩棟 顏色 不同 房子之間的 最大 距離。 第 …

一起了解原型模式

原型模式 原型模式&#xff0c;用起來其實就是做clone操作&#xff0c;clone一個對象&#xff0c;越過構造器&#xff0c;在特定使用場景下增加效率。 UML 使用場景&#xff1a; 類初始化需要消耗很多資源&#xff0c;比較耗時。new方式非常繁瑣&#xff0c;還涉及到權限之類的…