互联网项目特点:用户多、流量大,并发高、海量数据
集群:一个业务模块,部署在多台服务器上(多个人干同一件事)
分布式:一个大的业务系统,拆分成多个小的业务模块,分别部署在不同机器(多个人干不同事,组合起来就是一件大事)
架构演进:单体架构→垂直架构→分布式架构→SOA架构→微服务架构
【单体架构】就是所有的业务功能模块全放在一起,不进行拆分,简单粗暴,存在不少问题,【垂直架构】在此基础上解决问题,将单体架构中的多个模块拆分成多个【独立】的项目,形成多个单体架构,直到会导致不同独立的项目存在很多重复功能。【分布式架构】在此基础上,把公共业务模块抽取出来,作为独立的服务,供其他调用者消费,以实现服务的共享和重用。分布式架构也存在问题,服务提供方(公共模块)一旦发生变更,则所有消费方就需要跟着变更。【SOA架构】可以解决以上问题,它是一个组件模型,将不同功能单元进行拆分,通过ESB(企业服务总线,也叫【服务中介】),用于不同服务之间的交互。【微服务架构】是在SOA上做到升华,重点是业务的彻底组件化和服务化。原有的单个业务系统拆分为多个可以【独立】开发、设计、运行的小应用,再通过服务完成交互和集成。
微服务架构=80%SOA服务架构思想+100%的组件化架构思想+80%的领域建模思想。
特点:
1、服务实现组件化:开发者可以自由选择开发技术,不需要协调其他团队。
2、服务之间交互一般使用REST API
3、去中心化:每个微服务有自己的私有数据库持久化业务数据
4、自动化部署:把应用拆分成为一个一个独立的单个服务,方便自动化部署、测试、运维。
Dubbo:是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架,SOA时代的产物。
致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
RPC:远程过程调用
Dubbo快速入门:
1、zookeeper的下载和安装(开启服务,进入root,cd /opt/,cd zookeeper/,cd apache-zookeeper-3.5.6-bin,cd bin/ ./zkServer.sh start)
2、实现步骤:
1)创建服务提供者Provider模块
2)创建服务消费者Consumer模块
(以上两个模块都添加日志配置文件和依赖)
3)在服务提供者模块编写UserServiceImpl提供服务
4)在服务消费者中的UserController远程调用UserServiceImpl提供的服务
5)分别启动两个服务,测试
6)还需要创建一个模块,存放公共接口,作为本地bean资源,作为依赖被其他两个模块使用。
日志配置文件和依赖:
log4j.rootLogger=info,stdout,file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=../logs/iask.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %1 %m%n
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
1)创建服务提供者Provider模块:dubbo-service。本来有个接口,后放入那个模块中了。再实现该接口功能(服务),也就是步骤4)了。
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lcl</groupId>
<artifactId>dubbo-service</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包成war,为了能远程调用,下面还需要tomcat插件-->
<packaging>war</packaging>
<properties>
<spring.version>5.1.9.RELEASE</spring.version>
<dubbo.version>2.7.4.1</dubbo.version>
<zookeeper.version>4.0.0</zookeeper.version>
</properties>
<dependencies>
<!--servlet3.1.0规范的坐标-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--Dubbo的起步依赖,版本2.7之后统一为org.apache.dubb-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--注册中心-->
<!--Zookeeper客户端实现-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--Zookeeper客户端实现-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--公共接口做成一个模块,把其作为依赖-->
<dependency>
<groupId>com.lcl</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<!--构建-->
<build>
<!--设置插件-->
<plugins>
<!--具体的插件配置-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>9000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
spring配置文件:
<?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:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo
https://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--Dubbo的配置-->
<!--1、配置项目的名称,唯一-->
<dubbo:application name="dubbo-service"/>
<!--2、配置zookeeper的地址,即注册中心地址-->
<dubbo:registry address="zookeeper://192.168.23.129:2181"/>
<!--3、配置dubbo包扫描-->
<dubbo:annotation package="com.lcl.service.impl"/>
</beans>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--Spring-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
注意的点:【本来是jar包,现在为了能被远程调用,需要变成war包,同时加入web.xml,只需要spring】。pom文件中需要加入Dubbo的起步依赖。spring配置文件中加入Dubbo的配置,将服务在注册中心注册!!!!
2)创建服务消费者Consumer模块:在服务消费者中的UserController远程调用UserServiceImpl提供的服务,也就是步骤4)了
pom文件:dubbo-service不需要再依赖了,远程调用
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lcl</groupId>
<artifactId>dubbo-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>5.1.9.RELEASE</spring.version>
<dubbo.version>2.7.4.1</dubbo.version>
<zookeeper.version>4.0.0</zookeeper.version>
</properties>
<dependencies>
<!--servlet3.1.0规范的坐标-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--Dubbo的起步依赖,版本2.7之后统一为org.apache.dubb-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--Zookeeper客户端实现-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--Zookeeper客户端实现-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--远程调用,不再用本地依赖注入-->
<!-- <dependency>
<groupId>com.lcl</groupId>
<artifactId>dubbo-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>-->
<!--公共接口做成一个模块,把其作为依赖-->
<dependency>
<groupId>com.lcl</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<!--构建-->
<build>
<!--设置插件-->
<plugins>
<!--具体的插件配置-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
springmvc配置文件:
<?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:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://dubbo.apache.org/schema/dubbo
https://dubbo.apache.org/schema/dubbo/dubbo.xsd
">
<context:component-scan base-package="com.lcl.controller"/>
<mvc:annotation-driven/>
<!--Dubbo的配置-->
<!--1、配置项目的名称,唯一-->
<dubbo:application name="dubbo-web">
<!--因为在一台机器上运行的,会端口冲突,报错,需要改一下端口-->
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!--2、配置zookeeper的地址,即注册中心地址-->
<dubbo:registry address="zookeeper://192.168.23.129:2181"/>
<!--3、配置dubbo包扫描-->
<dubbo:annotation package="com.lcl.controller"/>
</beans>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--SpringMVC-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--读取spring-mvc.xml配置,加载bean资源。拦截后,在bean资源中找@RequestMapping("/save"),
根据路径和内容匹配,然后执行对应的方法,以及根据方法的返回值跳转页面-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring/spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
注意的点:web.xml,只需要springmvc。pom文件中需要加入Dubbo的起步依赖。springmvc配置文件中加入Dubbo的配置,将服务在注册中心注册!!!!
两个模块重点:
就是都要在pom文件中需要加入Dubbo的起步依赖,然后在各自的配置文件中加入Dubbo的配置,将服务在注册中心注册!!!!
然后服务消费者需要的资源就不用在本地上拿了,去注册中心获取资源,远程调用。
注意的点:
1、dubbo-server不是web模块,本来是jar包,现在为了能被远程调用,需要变成war包,同时加入web.xml。
2、在服务消费者的类中资源注入不能再用@Autowired,这是本地资源注入,此时的UserService用远程调用,应该用@Reference,这是远程注入
@RestController
@RequestMapping("/user")
public class UserController {
//@Autowired 本地资源注入,此时的UserService用远程调用
/* @Reference:
1、从zookeeper注册中心获取userService的访问url
2、进行远程调用RPC
3、将结果封装为一个代理对象,给变量赋值
*/
@Reference//远程注入
private UserService userService;
@RequestMapping("/sayHello")
public String sayHello(){
return userService.sayHello();
}
}
3、服务提供者有两个@Service注解,两个注解不是同一个包下的,功能不同,此处(import org.apache.dubbo.config.annotation.Service;)是将这个类提供的方法(也就是服务)对外发布。将访问的地址IP,端口,路径注册到注册中心中
package com.lcl.service.impl;
import com.lcl.service.UserService;
import org.apache.dubbo.config.annotation.Service;
//@Service spring容器内的bean资源
@Service //两个注解不是同一个包下的,功能不同,此处是将这个类提供的方法(也就是服务)对外发布。将访问的地址IP,端口,路径注册到注册中心中
public class UserServiceImpl implements UserService {
@Override
public String sayHello() {
return "hello dubbo~~~~~~";
}
}
4、会端口被占用的报错:在springmvc配置文件中修改成
<dubbo:application name="dubbo-web">
<!--因为在一台机器上运行的,会端口冲突,报错,需要改一下端口-->
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
5)分别启动两个服务,测试
先将公共接口的模块install,作为本地资源被其他两个模块依赖获取,然后先tomcat:run资源提供者,再tomcat:run资源消费者
Dubbo高级特性
1、dubbo-admin管理平台,是图形化的【服务管理】页面,从注册中心获取到所有的提供者/消费者进行管理。
dubbo-admin是一个前后端分离的项目,前端使用vue,后端使用springboot,所以安装它,其实就是部署该项目。
先安装node.js(傻瓜式安装即可),dubbo-admin先解压,再在dubbo-admin-develop\dubbo-admin-server\src\main\resources下的application.properties中把三个127.0.0.1改为192.168.23.129
然后在路径下cmd,或者shift加右击,在此处打开powershell窗口,再键入mvn clean package。等。。。。。。。
会报错,看收藏,需要增加一个插件
org.apache.maven.plugins maven-surefire-plugin true再到D:\Develop\dubbo-admin-develop\dubbo-admin-distribution\target 然后在路径下cmd,或者shift加右击,在此处打开powershell窗口,再键入java -jar .\dubbo-admin-0.4.jar 回车,即启动springboot 有个错一直没解决,只找到一个类似的,看收藏
Zookeeper概念:
是Apache Hadoop项目下的一个子项目,是一个树型目录服务。
是一个【分布式】的,【开源】的【分布式应用程序】的【协调服务】,也就是用来管理其他东东。
翻译过来就是动物管理员,管理Hadoop(大象)、Hive(蜜蜂)、Pig(小猪)的管理员,简称zk。
主要功能:
1、配置管理:到配置中心获取配置,若是需要变更,只需要变更配置中心
2、分布式锁:不同服务访问数据需要锁,可以到分布式锁那里拿
3、集群管理:作为注册中心,远程调用,管理服务
从以上三大功能可知,zookeeper就是用来统筹调度管理的
Zookeeper命令操作
1、Zookeeper数据模型
1)Zookeeper是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似,拥有一个层次化结构。这里面的每一个节点都被称为ZNode,每个节点都会保存自己的数据和节点信息。节点可以拥有子节点,同时也允许少量(1MB)数据存储在该节点之下。
节点可以分为四大类:1、PERSISTENT持久化节点 2、EPHEMERAL临时节点:-e 3、PERSISTENT_SEQUENTIAL持久化顺序节点:-s 4、EPHEMERAL_SEQUENTIALLI临时顺序节点:-es
下面两种命令都到/opt/zookeeper/apache-zookeeper-3.5.6-bin/bin目录下执行
2、服务端命令:./zkServer.sh start或stop或restart或status
3、客户端命令:
./zkCli.sh -server localhost:2181连接zookeeper的服务端
quit退出
ls /节点目录 列出节点目录下的子节点或子目录
create /节点 【信息】 创建节点,信息可有可无
get /节点 获取节点信息
set /节点 信息 设置节点信息
delete /节点 删除节点(不能删除有子节点的节点)
deleteall /节点 删除节点(能删除有子节点的节点)
help获取命令的使用帮助
create -e /节点 创建临时节点(什么参数都没有,则是持久化节点)
create -s /节点 创建持久化顺序节点
create -es /节点 创建临时顺序节点
ls2 等价于 ls -s,前者不推荐使用,使用后者,除了列出子节点,还有相应的信息
Zookeeper JavaAPI操作
1、Curator介绍:是Apache Zookeeper的Java客户端库
常见的Zookeeper Java API:原始的Java API 、ZkClient、Curator
2、常用操作:建立连接、增删改查节点、watch事件监听、分布式锁实现
日志配置文件:
log4j.rootLogger=off,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH/:mm/:ss}]%-5p %c(line/:%L) %x-%m%n
pom文件添加依赖和插件:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lcl</groupId>
<artifactId>curator-zk</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!--curator-->
<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>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
建立连接、增删改查节点:
package com.lcl.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
public class CuratorTest {
private CuratorFramework client;//提高作用域,但是建立连接那里的注解@Test要变成@Before,这样建立连接要先执行,client才能使用
/*建立连接*/
//@Test
@Before
public void testConnect(){
//newClient()需要四个参数:连接字符串(zk server的地址和端口:192.168.23.129:2181)、
// 会话超时时间(ms)、连接超时时间(ms)、重试策略
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
//1、方式一
/*CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.23.129:2181",
60 * 1000, 15 * 1000, retryPolicy);*/
//2、方式二
/* CuratorFramework client2 = CuratorFrameworkFactory.builder().connectString("192.168.23.129:2181").
sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).build();*/
/*整了个名称空间,默认在名称空间值对应的节点下操作,没有名称空间对应的节点则创建*/
client = CuratorFrameworkFactory.builder().connectString("192.168.23.129:2181").
sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).namespace("lcl").build();
//开启连接
client.start();
}
/*添加节点*/
@Test
public void testCreate1() throws Exception {
//1、基本创建
//注意:如果创建节点,没有指定数据,则默认将当前客户端的IP作为数据存储
client.create().forPath("/app1");
}
@Test
public void testCreate2() throws Exception {
//2、创建节点,带有数据
client.create().forPath("/app2","hehe".getBytes());
}
@Test
public void testCreate3() throws Exception {
//2、创建节点,设置节点类型
//默认类型是持久化,设置节点类型使用withMode(),参数指定类型
//临时节点是看不到的,因为后面会释放资源,一次会话结束,节点小时,可以在下面用个while true,死循环就行了
client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
/*while (true){}*/
}
@Test
public void testCreate4() throws Exception {
//2、创建多级节点
//creatingParentsIfNeeded()如果父节点不存在,则创建父节点
client.create().creatingParentsIfNeeded().forPath("/app4/p1");
}
/*查询节点*/
@Test
public void testGet1() throws Exception {
//1、查询数据:get
byte[] data = client.getData().forPath("/app1");
System.out.println(new String(data));
}
@Test
public void testGet2() throws Exception {
//2、查询子节点:ls
//因为设置了名称空间,\代表着\lcl,而不是根目录了
List<String> path = client.getChildren().forPath("/");
System.out.println(path);
}
@Test
public void testGet3() throws Exception {
//状态信息
Stat status = new Stat();
System.out.println(status);
//3、查询节点状态信息:ls -s
client.getData().storingStatIn(status).forPath("/app1");
System.out.println(status);
}
/*修改节点*/
@Test
public void testSet1() throws Exception {
//1、修改节点数据
client.setData().forPath("/app1","xqy".getBytes());
}
@Test
public void testSet2() throws Exception {
//获取节点状态信息
Stat status = new Stat();
client.getData().storingStatIn(status).forPath("/app1");
//从状态信息里获取version
int version = status.getVersion();
System.out.println(version);
//2、根据版本修改
client.setData().withVersion(version).forPath("/app1","xqy".getBytes());
}
/*删除节点*/
@Test
public void testDelete1() throws Exception {
//1、删除单个节点
client.delete().forPath("/app1");
}
@Test
public void testDelete2() throws Exception {
//2、删除带有子节点的节点
client.delete().deletingChildrenIfNeeded().forPath("/app4");
}
@Test
public void testDelete3() throws Exception {
//3、必须成功的删除
client.delete().guaranteed().forPath("/app2");
}
@Test
public void testDelete4() throws Exception {
//4、回调
client.delete().guaranteed().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
System.out.println("我被删除了");
System.out.println(curatorEvent);
}
}).forPath("/app2");
}
/*释放资源*/
@After
public void close(){
if(client != null){
client.close();
}
}
}
watch事件监听:Zookeeper允许用户在指定节点上注册一些watch,并且在一些待定事件触发的时候,Zookeeper服务端会将事件通知到感兴趣的客户端上去,该机制是Zookeeper实现分布式协调服务的重要特性。
Zookeeper中引入了watch机制来实现了发布/订阅功能,能够让多个订阅者同时监听某一个对象,当一个对象自身状态发生变化时,会通知所有订阅者。Curator引入Cache来实现监听。
Zookeeper提供了三种watch:NodeCache:监听某一特定节点,PathChildrenCache:监听一个ZNode的子节点(自己监听不到),TreeCache:可以监听整个树上的所有节点,类似上面两个的组合。
package com.lcl.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
public class CuratorWatcherTest {
private CuratorFramework client;//提高作用域,但是建立连接那里的注解@Test要变成@Before,这样建立连接要先执行,client才能使用
/*建立连接*/
//@Test
@Before
public void testConnect(){
//newClient()需要四个参数:连接字符串(zk server的地址和端口:192.168.23.129:2181)、
// 会话超时时间(ms)、连接超时时间(ms)、重试策略
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
//1、方式一
/*CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.23.129:2181",
60 * 1000, 15 * 1000, retryPolicy);*/
//2、方式二
/* CuratorFramework client2 = CuratorFrameworkFactory.builder().connectString("192.168.23.129:2181").
sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).build();*/
/*整了个名称空间,默认在名称空间值对应的节点下操作,没有名称空间对应的节点则创建*/
client = CuratorFrameworkFactory.builder().connectString("192.168.23.129:2181").
sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).namespace("lcl").build();
//开启连接
client.start();
}
/*演示NodeCache:监听某一特定节点*/
@Test
public void testNodeCache() throws Exception {
//1、创建NodeCache对象
NodeCache nodeCache = new NodeCache(client,"/app1");
//2、注册监听
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("节点变化了");
//获取变化后节点的数据
byte[] data = nodeCache.getCurrentData().getData();
System.out.println(new String(data));
}
});
//3、开启监听,如果设置为true,则开启监听器,加载缓存数据
nodeCache.start(true);
while (true){}
}
/*演示PathChildrenCache:监听一个ZNode的子节点*/
@Test
public void testPathChildrenCache() throws Exception {
//1、创建PathChildrenCache对象
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app4", true);
//2、注册监听
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
System.out.println("子节点变化了");
System.out.println(pathChildrenCacheEvent);
//监听子节点的数据变更,并且拿到变更后的数据
//1、获取类型
PathChildrenCacheEvent.Type type = pathChildrenCacheEvent.getType();
//2、判断类型是否是update
if(type.equals( PathChildrenCacheEvent.Type.CHILD_UPDATED)){
byte[] data = pathChildrenCacheEvent.getData().getData();
System.out.println(new String(data));
}
}
});
//3、开启监听,如果设置为true,则开启监听器,加载缓存数据
pathChildrenCache.start();
while (true){}
}
/*演示TreeCache:可以监听整个树上的所有节点,类似上面两个的组合。*/
@Test
public void testTreeCache() throws Exception {
//1、创建TreeCache对象
TreeCache treeCache = new TreeCache(client, "/app4");
//2、注册监听
treeCache.getListenable().addListener(new TreeCacheListener(){
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
System.out.println("节点变化了");
System.out.println(treeCacheEvent);
}
});
//3、开启监听,如果设置为true,则开启监听器,加载缓存数据
treeCache.start();
while (true){}
}
/*释放资源*/
@After
public void close(){
if(client != null){
client.close();
}
}
}
分布式锁:处理跨机器的进程之间的数据同步问题
实现有三种方式:基于缓存实现分布式锁(Redis…)、zookeeper(curator)、数据库层面实现分布式锁(悲观锁、乐观锁)
原理:
核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点
1、客户端获取锁时,在lock节点下创建【临时】【顺序】节点。
2、然后获取lock下面的所有子节点,客户端【获取】到【所有】的子节点后,如果发现自己创建的子节点【序号最小】,那么就认为该客户端获取了锁。使用完锁后,将该节点删除
3、如果发现自己创建的节点【并非】lock所有子节点中【最小的】,说明自己还没有获取到锁,此时客户端需要找到那个【比自己序号小的节点】,同时对其【注册事件监听器】,监听【删除事件】
4、如果发现比自己小的那个节点被删除,则客户端的watch监听器会收到通知,此时再次判断自己创建的节点是否是lock所有子节点中序号最小,如果是,则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听器
以上的总结:每个客户端要获取锁,都要创建临时顺序节点,然后每个客户都获取lock下所有子节点,比较,谁创建的子节点最小谁获取锁,用完锁删除对应节点,那么剩下的客户端需要对比自己序号小的一个节点注册监听器,一旦接收到删除事件的通知,那么就获取锁了。按序号排队队,对序号小的节点监听,别人一旦用完锁就删除,监听到删除,就轮到了自己获取锁。
curator实现分布式锁API:
有五种方案(做案例时用第二种):
1、InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
2、InterProcessMutex:分布式可重入排它锁
3、InterProcessReadWriteLock:分布式读写锁
4、InterProcessSemaphoreV2:共享信号量
分布式锁案例——模拟12306售票
客户端:
package com.lcl.curator;
public class LockTest {
public static void main(String[] args) {
Ticket12306 ticket12306 = new Ticket12306();
//创建客户端
Thread client1 = new Thread(ticket12306, "携程");
Thread client2 = new Thread(ticket12306, "飞猪");
client1.start();
client2.start();
}
}
在资源处设置锁
package com.lcl.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import java.util.concurrent.TimeUnit;
public class Ticket12306 implements Runnable{
private int tickets = 10;//数据库的票数
private InterProcessLock lock;
//在构造方法里初始化锁
public Ticket12306() {
//建立连接
//重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
//1、方式一
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.23.129:2181",
60 * 1000, 15 * 1000, retryPolicy);
//开启连接
client.start();
//初始化锁
lock = new InterProcessMutex(client,"/lock1");
}
@Override
public void run() {
while (true){
try {
//获取锁
lock.acquire(3,TimeUnit.SECONDS);
if(tickets > 0){
System.out.println(Thread.currentThread()+":"+tickets);
Thread.sleep(100);
tickets--;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//释放锁(必须释放锁,所以放在finally)
try {
lock.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
1、zookeeper集群介绍
1)Leader选举:
(1)Serverid:服务器ID
比如有三台服务器,编号分别是1,2,3.【编号越大】,在选择算法中的【权重越大】。
(2)Zxid:数据ID
服务器中存放的最大数据ID,【值越大】说明数据【越新】,在选择算法中【数据越新】【权重越大】。
在Leader选举的过程中,如果某台zookeeper获得了超过半数的选票,则此zookeeper就可以成为Leader了。
2、zookeeper集群搭建:看视频吧,也就是把之前的安装zookeeper和配置做三遍,还有配置一些细微的差别,最后启动三个zookeeper服务
zookeeper集群角色:
1、Leader领导者:处理事务请求、集群内部各服务器的调度者
2、Follower跟随者:处理客户端非事务请求,转发事务请求给Leader服务器、5参与Leader选举投票
3、Observer观察者:处理客户端非事务请求,转发事务请求给Leader服务器