0%

模式分类

创建型模式:

单例模式、工厂模式,抽象工厂模式,建造者模式,原型模式

结构型模式

从程序的结构上实现松耦合,从而扩大整体的类结构,用来解决更大的问题

适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式

行为型模式

模版方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式

OOP七大原则

  • 开闭原则

    对扩展开放,对修改关闭

  • 里氏替换原则

    继承必须确保超类所拥有的性质在子类中仍成立

  • 依赖倒置原则

    要面向接口编程,不要面向对象编程

  • 单一职责原则

    控制类的粒度大小,将对象解耦,提高其内聚性

  • 接口隔离原则

    要为各个类建立它们需要的专用接口

  • 迪米特法则

    只与直接朋友交谈,不跟“陌生人”说话

  • 合成复用原则

    尽量先使用组合或聚合等关联关系来实现,其次才考虑使用继承关系来实现

什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作

元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:

Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
生成流

阅读全文 »

方法引用
方法引用通过方法的名字来指向一个方法。

方法引用场景
方法引用和Lambda都需要函数式接口

引用的方法 必须 和函数式接口中的抽象方法,参数列表和返回值一致(方法名不需要一致)

即:如果发现某个类中有一个方法,和函数式接口抽象方法,参数和返回值都一样,那就可以直接引用该方法

方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

方法引用语法
方法引用使用一对冒号 :: 。

::方法名

::JDK 1.8 提出的新运算符—引用运算符。

作用:将某个方法引用过了,传递给函数式接口,接口调用方法时,就是调用引用来的方法

构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:

1
2
final Car car = Car.create( Car::new ); 
final List< Car > cars = Arrays.asList( car );

静态方法引用:它的语法是Class::static_method,实例如下:

1
cars.forEach( Car::collide );

特定类的任意对象的方法引用:它的语法是Class::method实例如下:

1
cars.forEach( Car::repair );

特定对象的方法引用:它的语法是instance::method实例如下:

1
2
final Car police = Car.create( Car::new ); 
cars.forEach( police::follow );

Single Node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# 下载
$ docker pull elasticsearch:5.6.8
#启动,需要配置JVM,否则报OOM
# 9200为 ES节点 和 外部 通讯使用
# 9300是tcp通讯端口,集群间和TCPClient走它
# 单点模式
$ docker run -d -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:5.6.8

#如果要使用kibana,最好先在此开启映射,否则需要更改container内部json数据
# 单点模式
$ docker run -d -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" --name es -p 9200:9200 -p 9300:9300 -p 5601:5601 -e "discovery.type=single-node" elasticsearch:5.6.8



# 访问 localhost:9200 可以看到JSON格式数据
####################################################
{
"name" : "wCWV0cR",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "5krSWTgVQUuxvyQvF8LCTw",
"version" : {
"number" : "5.6.8",
"build_hash" : "688ecce",
"build_date" : "2018-02-16T16:46:30.010Z",
"build_snapshot" : false,
"lucene_version" : "6.6.1"
},
"tagline" : "You Know, for Search"
}
####################################################

# 进入容器,安装ik
$ docker exec -it es /bin/bash

# 要安装对应版本
root@b340ba8042f5:/usr/share/elasticsearch# ./bin/elasticsearch -version
Version: 5.6., Build: cfe3d9f/2018-09-10T20:12:43.732Z, JVM: 1.8.0_181

# 解决跨域问题
# container没有vi,使用echo添加到最后
root@a604792a79ad:/usr/share/elasticsearch# cd config/
root@a604792a79ad:/usr/share/elasticsearch/config# echo "http.cors.enabled: true">> elasticsearch.yml
root@a604792a79ad:/usr/share/elasticsearch/config# echo -e "http.cors.allow-origin: \"*\"">> elasticsearch.yml


# 解决宿主机无法访问问题
# 让所有IP都可访问
root@a604792a79ad:/usr/share/elasticsearch/config# sed -i 's/^#\(transport.host: 0.0.0.0\)/\1/' elasticsearch.yml


# 查看是否写入成功
root@a604792a79ad:/usr/share/elasticsearch/config# cat elasticsearch.yml
http.host: 0.0.0.0
# Uncomment the following lines for a production cluster deployment
transport.host: 0.0.0.0
#discovery.zen.minimum_master_nodes: 1
http.cors.enabled: true
http.cors.allow-origin: "*"
network.host: 127.0.0.1


# 下载对应版本的ik
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.8/elasticsearch-analysis-ik-5.6.8.zip

# 后面分词器用的上,先配置
# 配置自己的扩展字典
# 配置自己设置的停止词字典
# sed 用法 在某个单词后添加某语句
root@fa9104ef06a1:/usr/share/elasticsearch/plugins/ik/config# sed -i 's/<entry key="ext_dict">/&ext.did/' IKAnalyzer.cfg.xmllocation</entry> -->
root@fa9104ef06a1:/usr/share/elasticsearch/plugins/ik/config# sed -i 's/<entry key="ext_stopwords">/&stopword.dic/' IKAnalyzer.cfg.xml
root@fa9104ef06a1:/usr/share/elasticsearch/plugins/ik/config# cat IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">ext.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords">stopword.dic</entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
root@fa9104ef06a1:/usr/share/elasticsearch/plugins/ik/config#

# 配置ik/config/stopword.dic,添加自定义停止词
# 在ik/config/目录下添加ext.dic文件,并自定义扩展词语

# 重启container
$ docker restart es

# head图形化界面
# 下载镜像(head属于本地端,可以直接本机安装)
$ docker pull mobz/elasticsearch-head:5
# 启动
$ docker run -d -p 9100:9100 docker.io/mobz/elasticsearch-head:5
#访问 localhost:9100 ,可以连接上9200的es

# kibana数据显示页面,需要与ES版本匹配
# 注意,此处使用了 --network ,因安全限制无法再暴露端口,若要使用则由es暴露端口,建议在es启动时直接暴露
$ docker run -it -d -e ELASTICSEARCH_URL=http://127.0.0.1:9200 --name kibana --network=container:es kibana:5.6.8

# 若ES未暴露Port,又不想重新经历步骤,只能修改container内部json数据
# 停止容器->修改两个json文件->停止docker->启动docker->启动容器。否则container会恢复为旧版本
# macOS 需screen进入tty
$ vim /var/lib/docker/containers/[hash_of_the_container]/hostconfig.json
# 在PortBindings中添加
"5601/tcp": [{"HostIp": "","HostPort": "5601"}]

$ vim /var/lib/docker/containers/[hash_of_the_container]/config.v2.json
# 在ExposedPorts中添加
"5601/tcp": {}

https://shipengliang.com/software-exp/docker-%E5%AE%B9%E5%99%A8%E7%9A%84%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%BC%8F.html

http://www.amd5.cn/atang_4301.html

安装mq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 下载
$ docker pull rabbitmq

# 启动
# 设置容器名Myrabbitmq
# 设置 用户名/密码 admin
# 映射网页端口 15672
# 映射 协议端口,基于此协议的客户端与消息中间件之间可以传递消息
$ docker run -dit --name Myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq

#未开启网页端口,需要进入容器开启
$ docker exec -ti 75d79fb9dc6f /bin/bash

#开启网页端
root@75d79fb9dc6f:/# rabbitmq-plugins enable rabbitmq_management

#访问 http://localhost:15672 即可

一.SpringCloud介绍
1.1 介绍
Spring Cloud是在Spring Boot的基础上构建的,用于简化分布式系统构建的工具集。该工具集为微服务架构中所涉及的配置管理、服务发现、智能路由、熔断器、控制总线等操作提供了一种简单的开发方式。也就是说Spring Cloud把非常流行的微服务的技术(组件)整合到一起,方便开发。

注册中心:Eureka
负载均衡:Ribbon
熔断器:Hystrix
服务通信:Feign
网关:Gateway
配置中心 :config
消息总线:Bus

阅读全文 »

一.应用架构的演进过程
目标

了解软件架构的演进过程

学习路径

主流的互联网技术特点
架构演变的过程
单体架构
垂直架构
分布式服务架构
SOA架构
微服务架构
1.1 主流的互联网技术特点
分布式 、高并发、集群、负载均衡、高可用。

分布式:一件事情拆开来做。

集群:一件事情所有服务器一起做。

负载均衡:将请求平均分配到不同的服务器中,达到均衡的目的。

高并发:同一时刻,处理同一件事情的处理能力(解决方案:分布式、集群、负载均衡)

高可用:系统都是可用的。

1.2. 架构演变的过程
1.2.1 单一应用架构(all in one)
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

架构优点:

架构简单,前期开发成本低、开发周期短,适合小型项目(OA、CRM、ERP 企业内部应用)。

架构缺点:

全部功能集成在一个工程中

业务代码耦合度高,不易维护。

维护成本高,不易拓展

并发量大,不易解决

技术栈受限,只能使用一种语言开发。

1.2.2 垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

架构优点:

业务代码相对解耦

维护成本相对易于拓展(修改一个功能,可以直接修改一个项目,单独部署)

并发量大相对易于解决(搭建集群)

技术栈可扩展(不同的系统可以用不同的编程语言编写)。

架构缺点:

功能集中在一个项目中,不利于开发、扩展、维护。

代码之间存在数据、方法的冗余

1.2.3 分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

架构优点:

业务代码完全解耦,并可实现通用

维护成本易于拓展(修改一个功能,可以直接修改一个项目,单独部署)

并发量大易于解决(搭建集群)

技术栈完全扩展(不同的系统可以用不同的编程语言编写)。

架构缺点:

缺少统一管理资源调度的框架

1.2.4 流动计算架构(SOA)
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

架构优点
业务代码完全解耦,并可实现通用
维护成本易于拓展(修改一个功能,可以直接修改一个项目,单独部署)
并发量大易于解决(搭建集群)
技术栈完全扩展(不同的系统可以用不同的编程语言编写)。
框架实现了服务治理,不去担心集群的使用情况(失败会尝试其它服务….)
1.2.5 小结
单体架构

全部功能集中在一个项目内(All in one)。

垂直架构

按照业务进行切割,形成小的单体项目。

分布式

应用调用服务,服务挂了,有其它可用,缺少服务治理

SOA架构

服务的提供者(以服务为主 service调用dao->数据库),消费者。
服务提供者与消费都注册到中心,由中心统一管理分配,失败重试,负载均衡。。。有服务治理
可以使用dubbo作为调度的工具(RPC协议), Zookeeper作为注册中心
微服务架构

将系统服务层完全独立出来,抽取为一个一个的微服务。 以功能为主(controller->service-dao->数据库 独立)
特点一:抽取的粒度更细,遵循单一原则,数据可以在服务之间完成数据传输(一般使用restful请求调用资源)。
特点二: 采用轻量级框架协议传输。(可以使用spring cloud)(http协议 restful json)
特点三: 每个服务都使用不同的数据库,完全独立和解耦 (分库分表)。
二.RPC(远程过程调用)
目标

了解什么是RPC

学习路径

RPC介绍

RPC组件

RPC调用

2.1 RPC介绍
​ Remote Procedure Call 远程过程调用,是分布式架构的核心,按响应方式分如下两种:

同步调用:客户端调用服务方方法,等待直到服务方返回结果或者超时,再继续自己的操作。

异步调用:客户端把消息发送给中间件,不再等待服务端返回,直接继续自己的操作。

是一种进程间的通信方式

它允许应用程序调用网络上的另一个应用程序中的方法

对于服务的消费者而言,无需了解远程调用的底层细节,是透明的

需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。

RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo、spring Cloud(restapi http)等。

2.2 RPC组件
RPC架构里包含如下4个组件:

客户端(Client):服务调用者
客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数打包成网络消息,再通过网络发送给服务方
服务端存根(Server Stub):接受客户端发送过来的消息并解包,再调用本地服务
服务端(Server):服务提供者。
2.3 RPC调用
服务调用方(client)调用以本地调用方式调用服务;
client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体在Java里就是序列化的过程
client stub找到服务地址,并将消息通过网络发送到服务端;
server stub收到消息后进行解码,在Java里就是反序列化的过程;
server stub根据解码结果调用本地的服务;
本地服务执行处理逻辑;
本地服务将结果返回给server stub;
server stub将返回结果打包成消息,Java里的序列化;
server stub将打包后的消息通过网络并发送至消费方;
client stub接收到消息,并进行解码, Java里的反序列化;
服务调用方(client)得到最终结果。
2.3.1 小结
RPC 远程过程调用: 一台电脑调用另一台电脑上的方法
RPC组件及调用过程 客户端->客户端存根(序列化(发送)与反序列化(接收),服务端信息)->服务端存根(接收-反序列,返回结果-序列化)->服务端真正的方法
调用方式:调用方用的是接口,服务方是真正的实现
三.Apache Dubbo概述
目标

了解什么是dubbo

学习路径

dubbo简介
dubbo架构
3.1. Dubbo简介
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。

Dubbo官网地址:http://dubbo.apache.org

Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

3.2. Dubbo架构

虚线都是异步访问,实线都是同步访问
蓝色:在启动时完成的功能
红色虚线(实线)都是程序运行过程中执行的功能

节点 角色名称
Provider 暴露服务的服务提供方 服务方
Consumer 调用远程服务的服务消费方 调用方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器
调用关系说明:

服务容器负责启动,加载,运行服务提供者。
服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者在启动时,向注册中心订阅自己所需的服务。
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
什么是长连接

长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是短连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,下次处理时直接发送数据包就OK了,不用建立TCP连接。

3.3 小结
dubbo 远程调用框架 RPC

dubbo架构

角色: 客户端与服务提供方 监控器注册中心

组员:服务消费端(调用者),服务提供方(容器), 注册中心, 监控中心(负载均衡)

四.Dubbo快速开发
目标

使用dubbo,完成服务消费者,调用,服务提供者方法

操作路径

环境准备
创建父工程(dubbo_parent) 依赖管理
创建公共子模块(dubbo_common) 实体类
创建接口子模块(dubbo_interface)
创建服务提供者模块(dubbo_provider) 对接口的实现
创建服务消费者模块(dubbo_consumer) 引用接口
Zookeeper中存放Dubbo服务结构(注册中心)
讲解

Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程调用,服务提供者、服务消费者会使用共同的接口,故本小节先创建一个父工程,父工程下有4个子模块,一个是公共子模块,一个是接口模块,一个是服务提供者模块,一个是服务消费者模块。通过Dubbo来实现服务消费方远程调用服务提供方的方法。

实现的业务为:根据id查询用户对象

业务描述:页面发送请求:user/findById.do?id=1 根据id从数据库获取用户对象

实现步骤:

环境准备:创建数据库,创建t_user表
创建父工程,基于maven,打包方式为pom,工程名:dubbo_parent
创建公共子模块,创建user实体类,打包方式为jar,工程名:dubbo_common
创建接口子模块,在父工程的基础上,打包方式为jar,模块名:dubbo_interface
创建服务提供者子模块,在父工程的基础上,打包方式为war,模块名:dubbo_provider
创建服务消费者模子块,在父工程的基础上,打包方式为war,模块名:dubbo_consumer
4.1 环境准备

1
2
3
4
5
6
7
8
9
10
11
create database itcastdubbo;

CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)
INSERT INTO t_user(username,age) VALUES("张三",22);
INSERT INTO t_user(username,age) VALUES("李四",20);
INSERT INTO t_user(username,age) VALUES("王五",25);

4.2 创建父工程
父工程,不实现任何代码,主要是添加工程需要的库的依赖管理(DependencyManagement),依赖管理就是解决项目中多个模块间公共依赖的版本号、scope的控制范围。

本项目需要使用spring-webmvc,使用dubbo(务必2.6.2以上版本)、zookeeper及其客户端(curator-framework)、Spring、Mybatis依赖库。

创建结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
./
├── dubbo_common
│ ├── dubbo_common.iml
│ ├── pom.xml
│ └── src
│ ├── main
│ └── test
├── dubbo_consumer
│ ├── pom.xml
│ └── src
│ ├── main
│ └── test
├── dubbo_interface
│ ├── pom.xml
│ └── src
│ ├── main
│ └── test
├── dubbo_parent.iml
├── dubbo_provider
│ ├── pom.xml
│ └── src
│ ├── main
│ └── test
└── pom.xml
dubbo_parent pom.xml增加依赖

详细信息
4.3 公共子模块 —— dubbo-common
pom.xml

1
2
<artifactId>dubbo_common</artifactId>
<packaging>jar</packaging>

创建User实体类

1
2
3
4
5
6
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
//此处省略getter/setter/toString
}

4.4 接口子模块 —— dubbo_interface
pom.xml

1
2
3
4
5
6
7
8
9
10
<artifactId>dubbo_interface</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

UserService

1
2
3
public interface UserService {
User findById(Integer id);
}
4.5 服务提供者模块
此模块是服务提供者模块,需要在容器启动时,把服务注册到zookeeper,故需要引入spring-webmvc,zookeeper及客户端依赖。

使用war 方式,需要依赖dubbo_interface

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
./
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── test
│ │ ├── dao
│ │ │ └── UserDao.java
│ │ └── service
│ │ └── impl
│ │ └── UserServiceImpl.java
│ ├── resources
│ │ ├── com
│ │ │ └── test
│ │ │ └── dao
│ │ │ └── UserDao.xml
│ │ ├── jdbc.properties
│ │ ├── spring-dao.xml
│ │ ├── spring-provider.xml
│ │ └── spring-service.xml
│ └── webapp
│ └── WEB-INF
│ └── web.xml
└── test
└── java

4.5.1 配置dubbo_provider依赖
pom.xml

详细信息
4.5.2 初始化java资源目录
UserDao

1
2
3
public interface UserDao {
User findById(Integer id);
}

UserServiceImpl

1
2
3
4
5
6
7
8
@Service
public class UserServiceImpl implements UserService {@Autowired
private UserDao userDao;

@Override
public User findById(Integer id) {
return userDao.findById(id);
}

}
在resource下创建com/test/dao目录,创建UserDao接口的映射文件

1
2
3
4
5
6
7
8
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.dao.UserDao">

<?xml version="1.0" encoding="utf-8" ?>
<select id="findById" resultType="com.test.pojo.User" parameterType="int">
select * from t_user where id = #{id}
</select>
4.5.3 初始化resources目录 spring-dao.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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">
<!-- 加载jdbc配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 工厂 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.test.pojo"/>
</bean>
<!-- dao扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test.dao"/>
</bean>
</beans>

jdbc.properties

1
2
3
4
5
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/itcastdubbo
jdbc.user=root
jdbc.password=root
spring-service.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事务注解 -->
<tx:annotation-driven/>
<!-- 注入dao -->
<import resource="classpath:spring-dao.xml"/>
</beans>

spring-provider.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 发布服务的名称 -->
<dubbo:application name="dubbo_provider"/>
<!-- 注册中心zookeeper:-->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- service: 注册上去服务
interface: 发布服务的接口
ref: spring容器的bean对象
将来通过这个interface调用服务时,就来调用spring容器中的对象的方法
-->
<dubbo:service interface="com.test.service.UserService" ref="userService"/>
<!-- 服务真正的执行者 -->
<bean id="userService" class="com.test.service.impl.UserServiceImpl"/>
<!-- 注入spring-service.xml -->
<import resource="classpath:spring-service.xml"/>

<!--发布dubbo协议,默认端口20880-->
<dubbo:protocol name="dubbo" port="20881"/>
<!-- 超时时间 -->
<dubbo:provider timeout="100000"/>
log4j.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
### direct log messages to stdout ###

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###

log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:\\mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout

4.5.4 启动项目
只要能启动spring的容器(加载spring的配置文件)就可以启动项目,因此有2种方式

ClassPathXmlApplication

1
2
3
4
5
6
public class ProviderApplication {
public static void main(String[] args) throws IOException {
new ClassPathXmlApplicationContext("classpath:spring-provider.xml");
System.in.read();
}
}

监听器与springMVC

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 方式一: listener 启动spring容器
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
-->
<!-- 方式二: 启动mvc的核心控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-provider.xml</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
4.5.5 注册中心验证 启动zookeeper,作为dubbo的注册中心

登录zookeeper客户端,直接查看ls /dubbo/com.test.service.UserService节点

1
2
[zk: localhost:2181(CONNECTED) 34] ls /dubbo/com.test.service.UserService
[configurators, providers]

如果 /dubbo下面没有这个节点,说明没有注册上,

如果有,内容是空,说明已经掉线

正常注册并连接在线

1
2
[zk: localhost:2181(CONNECTED) 35] ls /dubbo/com.test.service.UserService/providers
[dubbo%3A%2F%2F10.254.4.87%3A20880%2Fcom.test.service.UserService%3Fanyhost%3Dtrue%26application%3Ddubbo_provide%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.test.service.UserService%26methods%3DfindById%26pid%3D33771%26revision%3D1.0-SNAPSHOT%26side%3Dprovider%26timestamp%3D1592563739288]

注意:

消费者与提供者应用名称不能相同

如果有多个服务提供者,名称不能相同,通信端口也不能相同

只有服务提供者才会配置服务发布的协议,默认是dubbo协议,端口号是20880

可以在spring-dubbo.xml中配置协议的端口

4.6 服务消费者模块
此模块是服务消费者模块,此模块基于是Web应用,需要引入spring-webmvc,需要在容器启动时,去zookeeper注册中心订阅服务,需要引入dubbo、zookeeper及客户端依赖。

4.6.1 子模块dubbo_consumer导包
pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<artifactId>dubbo_consumer</artifactId>
<packaging>war</packaging>

<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
</dependencies>

4.6.2 初始化java资源目录
UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/user")
public class UserController {

@Reference(loadbalance = "roundrobin")
private UserService userService;

@RequestMapping("/findById")
public User findById(Integer id){
// 调用服务
User user = userService.findById(id);
// 返回json数据
return user;
}

}
4.6.3 初始化resources资源目录
spring-dubbo.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 发布的名称 -->
<dubbo:application name="dubbo_consumer"/>
<!-- 注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 服务订阅扫包
(2.6.0 事务问题)使用了服务的那controller的包, springmvc中的controller也不需要扫包了
<dubbo:reference>就不要了
在controller的服务注入使用@Reference(dubbo)
-->
<dubbo:annotation package="com.test"/>
<!-- id的值必须与controller中的@autowired的属性名称要一致
<dubbo:reference interface="com.test.service.UserService" id="userService"/>
-->
<!-- 启动时是否检查服务提供者是否存在,true: 则会检查【上线时】,没有则报错。false不检查
retries: 失败后的重试次数
-->
<dubbo:consumer check="false" timeout="2000" retries="2"/>
</beans>

spring-mvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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:mvc="http://www.springframework.org/schema/mvc"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫controller-->
<context:component-scan base-package="com.test.controller"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 引入dubbo配置文件-->
<import resource="classpath:spring-dubbo.xml"/>
</beans>

log4j.properties

之前的配置文件直接复制即可

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>4</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

4.6.5 启动服务消费者并测试
访问:localhost:8081/dubbo_consumer/user/findById?id=3
显示:{“id”:3,”username”:”C”,”age”:25}
注意:因为是RPC的框架,要求传递的参数和实体类要实现序列化

参数:Integer类型(实现序列化接口java.io.Serializable)

返回值:User(实现序列化接口java.io.Serializable),如果不进行序列化,抛出异常

4.7 Zookeeper中存放Dubbo服务结构(作为Dubbo运行的注册中心)
Zookeeper树型目录服务:

流程说明

服务提供者(Provider)启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址

服务消费者(Consumer)启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址

监控中心(Monitor)启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址

4.8 小结
dubbo_parent 版本控制
dubbo_common 实体类
dubbo_interface 共用接口
dubbo_provider 服务提供者 dao(spring-dao.xml 整合mybatis), 事务(spring-service.xml), 注册到注册中心(spring-prodvider.xml)
启动方式: 3种 推荐开发中用main方法类,tomcat来启动(监听器,springmvc的方式)

dubbo_consumer controller接收参数调用服务接口 spring-mvc.xml, 注册到注册中心 spring-dubbo.xml web.xml dispatcherServlet tomcat来启动

子模块工程间的关系

dubbo_consumer依赖于dubbo_interface(依赖于common) war tomcat
dubbo_provider(service,dao)依赖于dubbo_interface(依赖于common) war tomcat
五.Dubbo管理控制台
我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,部署到tomcat即可。

目标

Dubbo管理控制台的使用(即Dubbo监控中心)

使用路径

安装(dubbo-admin.war)
使用(dubbo-admin.war)

5.1. 安装
下载dubbo-admin.war,放入Tomcat的webapps中

1
2
$ find ./apache-tomcat-8.5.54 -name "dubbo*war*" -type f    
./apache-tomcat-8.5.54/webapps/dubbo-admin.war

启动Tomcat,此war文件会自动解压

1
2
3
4
5
6
7
$ ./apache-tomcat-8.5.54/bin/startup.sh
Using CATALINA_BASE: /Users/swzxsyh/Program/apache-tomcat-8.5.54
Using CATALINA_HOME: /Users/swzxsyh/Program/apache-tomcat-8.5.54
Using CATALINA_TMPDIR: /Users/swzxsyh/Program/apache-tomcat-8.5.54/temp
Using JRE_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home
Using CLASSPATH: /Users/swzxsyh/Program/apache-tomcat-8.5.54/bin/bootstrap.jar:/Users/swzxsyh/Program/apache-tomcat-8.5.54/bin/tomcat-juli.jar
Tomcat started.

修改WEB-INF下的dubbo.properties文件

1
2
3
4
5
6
# 注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号

dubbo.registry.address=zookeeper://localhost:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
重启tomcat

5.2. 使用
访问

1
localhost:8080/dubbo-admin/

账号密码都为admn或都为guest,上方配置文件中配置的

启动服务提供者工程和服务消费者工程,可以在查看到对应的信息

5.3 小结
安装(dubbo-admin.war),放置到tomcat webapp目录下,zookeeper不是本地则要修改WEB-INF下的dubbo.properties文件, 改zookeeper的ip地址。

使用(dubbo-admin.war)

访问localhost:8080/dubbo-admin/,输入用户名(root)和密码(root)

六.Dubbo相关配置说明
目标

Dubbo相关配置说明

路径

包扫描(dubbo注解配置)

服务接口访问协议dubbo协议

rmi协议

启动时检查

超时调用

6.1. 包扫描
1
<dubbo:annotation package=”com.test.service” />
服务提供者和服务消费者前面章节实现都是基于配置文件进行服务注册与订阅,如果使用包扫描,可以使用注解方式实现,推荐使用这种方式。

6.1.1 服务提供者,使用注解实现
在spring-dubbo.xml中配置

1
<dubbo:annotation package=”com.test.service” />
服务提供者和服务消费者都需要配置,表示包扫描,作用是扫描指定包(包括子包)下的类。

同时去掉以下配置:

1
2
3
4
5
6
<!--指定暴露的服务接口及实例-->
<dubbo:service interface="com.test.service.UserService"
ref="userSerivce"/>
<!--配置业务类实例-->
<bean id="userSerivce"
class="com.test.service.impl.UserServiceImpl"/>

在Controller类中使用@Reference注解

1
2
@Reference(loadbalance = "roundrobin")
private UserService userService;

注意:其中@Reference是dubbo包下(com.alibaba.dubbo.config.annotation.Reference)的注解。表示订阅服务

6.1.3 重启服务测试使用
重启服务提供者模块 dubbo-provider

重启服务消费者模块 dubbo-consumer

在浏览器输入测试URL:localhost:8081/dubbo_consumer/user/findById?id=3

6.2 服务接口访问协议【提供方修改】
1
<dubbo:protocol name=”dubbo” port=”20881”>
一般在服务提供者一方配置,可以指定使用的协议名称和端口号。

其中Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。

推荐使用的是dubbo协议,默认端口号:20880。

dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量、大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。

也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议, 在spring-provider.xml配置文件中添加:

1
2
3
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20881" />
<dubbo:protocol name="rmi" port="1099" />

dubbo协议:

连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用
rmi协议:

连接个数:多连接
连接方式:短连接
传输协议:TCP
传输方式:同步传输
序列化:Java标准二进制序列化
适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。
适用场景:常规远程服务方法调用,与原生RMI服务互操作
详情使用可通过博客文章:https://www.cnblogs.com/duanxz/p/3555876.html了解

6.3 启动时检查
1
<dubbo:consumer check=”false”/>
上面这个配置需要配置在服务消费者一方,如果不配置默认check值为true。Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题。可以通过将check值改为false来关闭检查。

建议在开发阶段将check值设置为false,在生产环境下改为true。

如果设置为true,启动服务消费者,会抛出异常,表示没有服务提供者

6.4. 超时调用
默认的情况下,dubbo调用的时间为一秒钟,如果超过一秒钟就会报错,所以我们可以设置超时时间长些,保证调用不出问题,这个时间需要根据业务来进行确定。

修改消费者 配置文件

1
2
3
4
5
6
7
8
<!--超时时间为10秒钟-->
<dubbo:consumer timeout="10000"></dubbo:consumer>
修改提供者配置文件

1
2
<!--超时时间设置为10秒钟-->
<dubbo:provider timeout="10000"></dubbo:provider>

6.5 小结
包扫描(dubbo注解配置)

<dubbo:annotation package=”com.test”>
服务提供方: @Service -> alibaba.dubbo包
消费方: controller, @Autowired=> @Reference-> alibaba.dubbo
服务接口访问协议

(服务提供者)

dubbo协议
rmi协议

<dubbo:protocol name=”dubbo” port=”20881”>

<dubbo:protocol name=”rmi” port=”1099”>
启动时检查

1
2
3
4
5
6
7
8
(服务消费者)
<dubbo:consumer check="false"></dubbo:consumer> 开发时。发布时一定为true超时调用

(服务消费者)
<dubbo:consumer check="false" timeout="10000"></dubbo:consumer>
(服务提供者)

<dubbo:provider timeout="10000"></dubbo:provider>

七.负载均衡
目标

Dubbo配置负载均衡

学习路径

负载均衡介绍
测试负载均衡效果
7.1. 介绍
负载均衡(Load Balance):其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。

在集群负载均衡时,Dubbo 提供了多种均衡策略(包括随机random、轮询roundrobin、最少活跃调用数leastactive),缺省【默认】为random随机调用。

配置负载均衡策略,既可以在服务提供者一方配置(@Service(loadbalance = “roundrobin”)),也可以在服务消费者一方配置(@Reference(loadbalance = “roundrobin”)),两者取一

如下在服务消费者指定负载均衡策略,可在@Reference添加@Reference(loadbalance = “roundrobin”)

1
2
3
4
5
6
@RestController
@RequestMapping("/user")
public class UserController {
@Reference(loadbalance = "roundrobin")
private UserSerivce userService;
}

7.2. 测试负载均衡效果
增加一个提供者,提供相同的服务;

正式生产环境中,最终会把服务端部署到多台机器上,故不需要修改任何代码,只需要部署到不同机器即可测试。下面我们通过启动 ProviderApplication 类来做测试

修改为轮询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/user")
public class UserController {

@Reference(loadbalance = "roundrobin")
private UserService userService;

@RequestMapping("/findById")
public User findById(Integer id){
// 调用服务
User user = userService.findById(id);
// 返回json数据
return user;
}
}

先启动一个ProviderApplication

IDEA——Edit Configuration——Add New Configuration——Application——进行配置

设置可以启动多个实例

勾选刚刚配置的Application中的 Allow parallel run

修改spring-provider.xml 端口依次改为20881,20882,20883

1
2

<dubbo:protocol name=”dubbo” port=”20881”/>
启动ProviderApplication,最终有3个,分别端口为20881,20882,20883

其中

1
2
@Reference(loadbalance = “roundrobin”) // 表示轮询
@Reference(loadbalance = “random”) // 表示随机(默认)
访问测试

启动消费者,访问:http://localhost:92/user/findById?id=3

7.3 小结
负载均衡: 把请求均匀分配到各服务提供者上

loadbalance:

random 随机 默认
roundrobin 轮循
leastactive 最少活跃数
consistenhash 一致哈希
八.配置中心
目标

Dubbo配置中心

路径

配置中心环境介绍

实现配置中心

(1)在Zookeeper中添加数据源所需配置

(2)在dubbo-common中导入jar包, 在这里实现配置中心的读取与监听

(3)删除数据库配置文件,数据库配置从Zookeeper上读取

添加watch机制

添加监听(数据库配置信息的节点, 节点数据变化时,重新设置数据库的配置信息,刷新容器)
获取Spring容器对象,再刷新
8.1. 环境介绍
​ 数据发布/订阅即所谓的配置中心:发布者将数据发布到ZooKeeper一系列节点上面,订阅者进行数据订阅,当数据有变化时,可以及时得到数据的变化通知,达到动态及时获取数据的目的。

现在项目中有两个提供者,配置了相同的数据源,如果此时要修改数据源,必须同时修改两个才可以。

我们可以将数据源中需要的配置信息配置存储在zookeeper中,如果修改数据源配置,使用zookeeper的watch机制,同时对提供者的数据源信息更新。

8.2. 实现配置中心
操作路径

在zookeeper中添加数据源所需配置
在dubbo-common中导入jar包

修改数据源,读取zookeeper中数据源所需配置数据

在dubbo-common中创建工具类:SettingCenterUtil,继承PropertyPlaceholderConfigurer
编写载入zookeeper中配置文件,传递到Properties属性中
重写processProperties方法
修改spring-dao.xml
watch机制

添加监听
获取容器对象,刷新spring容器:SettingCenterUtil,实现ApplicationContextAware
8.2.1 在zookeeper中添加数据源所需配置

1
2
3
4
5
6
7
8
9
10
11
[zk: localhost:2181(CONNECTED) 71] create /config 'config'
Created /config
[zk: localhost:2181(CONNECTED) 72] create /config/jdbc.url 'jdbc:mysql://localhost:3306/test_01'
Created /config/jdbc.url
[zk: localhost:2181(CONNECTED) 76] create /config/jdbc.user 'root
Created /config/jdbc.user
[zk: localhost:2181(CONNECTED) 77] create /config/jdbc.password 'root'
Created /config/jdbc.password
[zk: localhost:2181(CONNECTED) 78] create /config/jdbc.driver 'com.mysql.jdbc.Driver'
Created /config/jdbc.driver
[zk: localhost:2181(CONNECTED) 79]

8.2.2 在dubbo-common中导入jar包
详细信息
8.2.3 修改数据源,读取zookeeper中数据
8.2.3.1 在dubbo-common中创建工具类:SettingCenterUtil,继承PropertyPlaceholderConfigurer

1
2
3
4
5
6
7
8
9
10
$ cd dubbo_common/src/main/java   
~/P/i/d/d/s/m/java
$ tree
.
└── com
└── test
├── pojo
│ └── User.java
└── utils
└── SettingCenterUtil.java
1
public class SettingCenterUtil extends PropertyPlaceholderConfigurer 

8.2.3.2 编写载入zookeeper中配置文件,传递到Properties属性中
详细信息
8.2.3.3 重写processProperties方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**

* 处理properties内容,相当于此标签
* <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
* @param beanFactoryToProcess
* @param props
* @throws BeansException
*/
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
// 载入zookeeper配置信息,即从Zookeeper中获取数据源的连接信息
loadZk(props);
super.processProperties(beanFactoryToProcess, props);
}
  • 8.2.3.4 修改spring-dao.xml
1
2
3
4
<!--  注释  加载jdbc配置文件   -->
<!-- <context:property-placeholder location="classpath:jdbc.properties"/>-->
<!-- 添加 加载配置中心对象 -->
<bean class="com.test.utils.SettingCenterUtil"></bean>

8.2.4 watch机制
8.2.4.1 在SettingCenterUtil中添加监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**

* 添加对zookeeper中数据库配置的监听
@param props
*/
private void addWatch(Properties props){
//connectString 连接字符串 host:port
String connectString = "127.0.0.1:2181";
//sessionTimeoutMs session timeout 会话超时时间
int sessionTimeoutMs = 1000;
//connectionTimeoutMs connection timeout 连接超时时间
int connectionTimeoutMs = 1000;
//retryPolicy retry policy to use 重试策略
// baseSleepTimeMs 每次重试间隔时间
// 1.创建重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
// 2. 创建客户端
CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, sessionTimeoutMs, connectionTimeoutMs, retryPolicy);
// 3. 启动
client.start();// 启动

TreeCache treeCache = new TreeCache(client, "/config");
try {
treeCache.start();
// 添加监听
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
if(event.getType() == TreeCacheEvent.Type.NODE_UPDATED){
// 如果是jdbc.url的值变更
if(event.getData().getPath().equals("/config/jdbc.url")){
props.setProperty("jdbc.url",new String(event.getData().getData()));
}else if(event.getData().getPath().equals("/config/jdbc.driver")){
// 如果 jdbc.driver变更
props.setProperty("jdbc.driver",new String(event.getData().getData()));
}else if(event.getData().getPath().equals("/config/jdbc.user")){
// 用户名修改了
props.setProperty("jdbc.user",new String(event.getData().getData()));
}else if(event.getData().getPath().equals("/config/jdbc.password")){
// 密码修改了
props.setProperty("jdbc.password",new String(event.getData().getData()));
}
// 刷新spring容器
applicationContext.refresh();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

注意:

不要关闭client,否则无法进行监控
修改完成后必须刷新spring容器的对象
8.2.4.2 获取容器对象,刷新spring容器
修改SettingCenterUtil实现ApplicationContextAware接口,重写setApplicationContext方法,获取applicationContext对象。

AbstractApplicationContext容器对象父类,提供了refresh方法,可以刷新容器中的对象,故强制转换。

在processProperties的方法中,添加addWatch(props);

1
2
3
4
5
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
loadZk(props);
addWatch(props);
super.processProperties(beanFactoryToProcess, props);
}

修改Zookeeper的配置

1
2
3
[zk: localhost:2181(CONNECTED) 79] set /config/jdbc.url "jdbc:mysql:///test"
[zk: localhost:2181(CONNECTED) 80] get /config/jdbc.url
jdbc:mysql:///test

8.3 小结
配置中心环境介绍 回看zookeeper配置中心的图
实现配置中心
在Zookeeper中添加数据源所需配置
在dubbo-common中导入jar包
修改数据源,读取Zookeeper中数据,继承ProperteisPlaceHolderConfigurer.processProperties, 读取zk中的数据库配置,设置进props参数
watch机制
添加监听 TreeCache, event.type=node_update, 注意:判断path 不要关闭客户端
获取容器对象且刷新

一. 介绍zookeeper
目标
了解Zookeeper的概念
了解分布式的概念
学习路径
Zookeeper概述
Zookeeper的发展历程
什么是分布式
Zookeeper的应用场景
1.1 zookeeper概述
Apache ZooKeeper的系统为分布式协调是构建分布式应用的高性能服务。
ZooKeeper 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。
ZooKeeper 适用于存储和协同相关的关键数据,不适合用于大数据量存储。

1.2 zookeeper被大量使用
Hadoop:使用ZooKeeper 做Namenode 的高可用。
HBase:保证集群中只有一个master,保存hbase:meta表的位置,保存集群中的RegionServer列表。
Kafka:集群成员管理,controller 节点选举。
1.3 什么是分布式
1.3.1 集中式系统
集中式系统,集中式系统中整个项目就是一个独立的应用,整个应用也就是整个项目,所有的东西都在一个应用里面。部署到一个服务器上。
布署项目时,放到一个tomcat里的。也称为单体架构

1.3.2 分布式系统
多台计算机构成
计算机之间通过网络进行通信
彼此进行交互
共同目标
1.3.3 小结
集中式:开发项目时都是在同一个应用里,布署到同一个tomcat,只有一个tomcat即可

分布:分多个工程开发,布署多个tomcat里

1.4 zookeeper的应用场景
1.4.1 注册中心
分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构。通过调用Zookeeper提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。
Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。

1.4.2 配置中心
​ 数据发布/订阅即所谓的配置中心:发布者将数据发布到ZooKeeper一系列节点上面,订阅者进行数据订阅,当数据有变化时,可以及时得到数据的变化通知,达到动态获取数据的目的。

ZooKeeper 采用的是推拉结合的方式。

推: 服务端会推给注册了监控节点的客户端 Wathcer 事件通知

拉: 客户端获得通知后,然后主动到服务端拉取最新的数据

1.4.3 分布式锁——非公平锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

1.4.4 分布式队列——公平锁
在传统的单进程编程中,我们使用队列来存储一些数据结构,用来在多线程之间共享或传递数据。分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。

1.4.5 负载均衡
负载均衡是通过负载均衡算法,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。

每台工作服务器在启动时都会去ZooKeeper的servers节点下注册临时节点,每台客户端在启动时都会去servers节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出一台工作服务器,并与之建立网络连接

1.4.6 小结
Zookeeper概述
Zookeeper的发展历程
什么是分布式 应用是布署多台服务器上(多个tomcat)
Zookeeper的应用场景
注册中心(房产中介)
配置中心(多台应用(服务) 修改配置文件,不需要重启)
分布式锁 多个应用(服务)同一时刻,只有一个服务能够执行某个操作
分布式队列:使得多进程、多服务间可以同步数据、传输数据、协同工作
负载均衡:使用多应用的调用频率比较平均
二.zookeeper环境搭建
目标

安装Zookeeper

学习路径

Zookeeper的发展历程
什么是分布式
Zookeeper的应用场景
2.1 前提
安装Docker

2.2安装zookeeper
2.2.1 下载

1
2
3
4
5
6
#Docker拉取镜像
$ docker pull zookeeper
#查看镜像文件
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zookeeper latest 454af3da184c 6 days ago 252MB

2.2.2 修改配置文件

使用一个目录,创建zk配置文件/数据/日志目录

1
2
3
4
5
6
$ mkdir conf  data log
$ tree zk
zk
├── conf
├── data
└── log

暂时启动zookeeper

1
$ docker run -d --name zk --restart always zookeeper

复制配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ docker cp -a zk:/conf/zoo.cfg ~/Program/zk/conf/zoo.cfg    
$ cat ~/Program/zk/conf/zoo.cfg

dataDir=/data
dataLogDir=/datalog
tickTime=2000
initLimit=5
syncLimit=2
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
server.1=localhost:2888:3888;2181

停止并删除未配置的zookeeper容器

1
$ docker stop zk && docker rm zk

指定保存数据的目录:data目录

如果需要日志,可以创建log文件夹,指定dataLogDir属性

2.2.3 启动zookeeper

1
2
3
4
5
6
7
8
$ docker run -d --name zk --restart always \  
-p 2181:2181 -p 2888:2888 -p 3888:3888 \
-v /Users/swzxsyh/Program/zk/conf/zoo.cfg:/conf/zoo.cfg \
-v /Users/swzxsyh/Program/zk/data:/data \
-v /Users/swzxsyh/Program/zk/log:/datalog \
zookeeper

#zookeeper客户端端口,跟随端口,选择端口

2.2.4 启动客户端测试

下载客户端,启动

$ ./zkCli.sh
/usr/bin/java
2020-06-16 17:13:31,750 [myid:] - INFO [main:ClientCnxn@1653] - zookeeper.request.timeout value is 0. feature enabled=

省略中间输出…

出现此行即说明连接成功

Welcome to ZooKeeper!
2020-06-16 17:13:31,757 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1112] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2020-06-16 17:13:31,834 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@959] - Socket connection established, initiating session, client: /127.0.0.1:64028, server: localhost/127.0.0.1:2181
[zk: localhost:2181(CONNECTING) 0] 2020-06-16 17:13:31,896 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x100014a6eed0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
2.2.5 小结
pull镜像
创建data/conf/log目录
复制zoo.cfg文件并加载启动
三.zookeeper基本操作
目标

Zookeeper的客户端命令
Zookeeper的java的api操作
学习路径

Zookeeper的数据结构

节点的分类

持久性
临时性
客户端命令(创建、查询、修改、删除)

Zookeeper的java的api介绍(创建、查询、修改、删除)

Zookeeper的watch机制

NodeCache
PathChildrenCache
TreeCache
3.1 zookeeper数据结构
ZooKeeper 的数据模型是层次模型。层次模型常见于文件系统。

每个目录下面可以创建文件,也可以创建子目录,最终构成了一个树型结构。通过这种树型结构的目录,可以将文件分门别类的进行存放,方便我们后期查找。

层次模型和key-value 模型是两种主流的数据模型。ZooKeeper 使用文件系统模型主要基于以下两点考虑
文件系统的树形结构便于表达数据之间的层次关系。
文件系统的树形结构便于为不同的应用分配独立的命名空间(namespace 路径url)
ZooKeeper 的层次模型称作data tree。Datatree 的每个节点叫作znode(Zookeeper node)。不同于文件系统,每个节点都可以保存数据。每个节点都有一个版本(version)。版本从0 开始计数。

如图所示,data tree中有两个子树,用于应用1( /app1)和应用2(/app2)。

每个客户端进程pi 创建一个znode节点 p_i 在 /app1下, /app1/p_1就代表一个客户端在运行。

3.2 节点的分类
一个znode可以是持久性的,也可以是临时性的
持久性znode[PERSISTENT],这个znode一旦创建不会丢失,无论是zookeeper宕机,还是client宕机。
临时性的znode[EPHEMERAL],如果zookeeper宕机了,或者client在指定的timeout时间内没有连接server,都会被认为丢失。 -e
znode也可以是顺序性的,每一个顺序性的znode关联一个唯一的单调递增整数。这个单调递增整数是znode名字的后缀。
持久顺序性的znode(PERSISTENT_SEQUENTIAL):znode 处理具备持久性的znode的特点之外,znode的名称具备顺序性。 -s
临时顺序性的znode(EPHEMERAL_SEQUENTIAL):znode处理具备临时性的znode特点,znode的名称具备顺序性。-s
3.3 客户端命令
3.3.1 查询所有命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[zk: localhost:2181(CONNECTED) 8] help
ZooKeeper -server host:port cmd args
addauth scheme auth
close
config [-c] [-w] [-s]
connect host:port
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
delete [-v version] path
deleteall path
delquota [-n|-b] path
get [-s] [-w] path
getAcl [-s] path
history
listquota path
ls [-s] [-w] [-R] path
ls2 path [watch]
printwatches on|off
quit
reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,…]]] | [-add serverId=host:port1:port2;port3[,…]] [-remove serverId[,…]*]
redo cmdno
removewatches path [-c|-d|-a] [-l]
rmr path
set [-s] [-v version] path data
setAcl [-s] [-v version] [-R] path acl
setquota -n|-b val path
stat [-w] path
sync path
3.3.2 查询根路径下的节点
1
2
[zk: localhost:2181(CONNECTED) 9] ls /zookeeper
[config, quota]
3.3.3 创建普通永久节点
1
2
3
4
5
#创建HelloZK节点,值为Hello,Zookeeper
[zk: localhost:2181(CONNECTED) 10] create /HelloZK “Hello,Zookeeper”
Created /HelloZK
[zk: localhost:2181(CONNECTED) 11] ls /HelloZK
[]
3.3.4 创建带序号永久节点
1
2
[zk: localhost:2181(CONNECTED) 13] create -s /HelloZK_SEQ “ZK With SEQUENCE”
Created /HelloZK_SEQ0000000001
3.3.5 创建普通临时节点
1
2
3
4
5
6
7
8

-e:表示普通临时节点

[zk: localhost:2181(CONNECTED) 17] create -e /Hello_ZK_Normal “Normal Node”
Created /Hello_ZK_Normal
[zk: localhost:2181(CONNECTED) 19] ls /
[HelloZK, HelloZK_SEQ0000000001, Hello_ZK_Normal, zookeeper]

关闭客户端,再次打开查看 Hello_ZK_Normal 节点消失

[zk: localhost:2181(CONNECTED) 1] ls /
[HelloZK, HelloZK_SEQ0000000001, zookeeper]
3.3.6 创建带序号临时节点
1
2
3
4
5
6
7

-e:表示普通临时节点

-s:表示带序号节点

[zk: localhost:2181(CONNECTED) 2] create -e -s /Hello_ZK_SEQ_TEMP “Temp And Sequence”
Created /Hello_ZK_SEQ_TEMP0000000003

关闭客户端,再次打开查看 Hello_ZK_SEQ_TEMP 节点消失

[zk: localhost:2181(CONNECTED) 0] ls /
[HelloZK, HelloZK_SEQ0000000001, zookeeper]
3.3.7 查询节点数据
1
2
3
4
5
6
7
8
9
10
11
12
13
[zk: localhost:2181(CONNECTED) 7] get -s /HelloZK
Hello,Zookeeper
cZxid = 0x2
ctime = Tue Jun 16 17:28:46 CST 2020
mZxid = 0x2
mtime = Tue Jun 16 17:28:46 CST 2020
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 15
numChildren = 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

­­­­­­­­­­­节点的状态信息,也称为stat结构体­­­­­­­­­­­­­­­­­­­

创建该znode的事务的zxid(ZooKeeper Transaction ID)

事务ID是ZooKeeper为每次更新操作/事务操作分配一个全局唯一的id,表示zxid,值越小,表示越先执行

cZxid = 0x4454 # 0x0表示十六进制数0
ctime = Thu Jan 01 08:00:00 CST 1970 # 创建时间
mZxid = 0x4454 # 最后一次更新的zxid
mtime = Thu Jan 01 08:00:00 CST 1970 # 最后一次更新的时间
pZxid = 0x4454 # 最后更新的子节点的zxid
cversion = 5 # 子节点的变化号,表示子节点被修改的次数
dataVersion = 0 # 表示当前节点的数据变化号,0表示当前节点从未被修改过
aclVersion = 0 # 访问控制列表的变化号 access control list

如果临时节点,表示当前节点的拥有者的sessionId

ephemeralOwner = 0x0 # 如果不是临时节点,则值为0
dataLength = 13 # 数据长度
numChildren = 1 # 子节点的数量
3.3.8 修改节点数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[zk: localhost:2181(CONNECTED) 9] set /HelloZK “Modify HelloZK”

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/HelloZK
[zk: localhost:2181(CONNECTED) 10]
[zk: localhost:2181(CONNECTED) 10] get -s /HelloZK
Modify HelloZK
cZxid = 0x2
ctime = Tue Jun 16 17:28:46 CST 2020
mZxid = 0xc
mtime = Tue Jun 16 17:40:47 CST 2020
pZxid = 0x2
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
3.3.9 创建子节点
1
2
3
4
5
[zk: localhost:2181(CONNECTED) 36] create /TestDelete
Created /TestDelete
[zk: localhost:2181(CONNECTED) 37] create /TestDelete/RMR
Created /TestDelete/RMR
[zk: localhost:2181(CONNECTED) 38]
3.3.10 删除节点
1
2
3
[zk: localhost:2181(CONNECTED) 11] delete /HelloZK_SEQ0000000001
[zk: localhost:2181(CONNECTED) 12] ls /
[HelloZK, zookeeper]
3.3.11 递归删除节点
1
2
3
4
5
6
7
8

有子节点的使用delete会提示目录非空

[zk: localhost:2181(CONNECTED) 39] delete /TestDelete
Node not empty: /TestDelete

需用rmr命令删除,现已更新为deleteall

[zk: localhost:2181(CONNECTED) 12] rmr /TestDelete
The command ‘rmr’ has been deprecated. Please use ‘deleteall’ instead.
[zk: localhost:2181(CONNECTED) 13] ls /
[jsodjsd0000000011, sjdiosdjsoi, zookeeper]
3.3.12 查看节点状态
1
2
3
4
5
6
7
8
9
10
11
12
[zk: localhost:2181(CONNECTED) 2] stat /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
3.3.13 日志的可视化
日志存储路径

1
~/Program/zk/log/version-2
日志都是以二进制文件存储的,需要使用命令行查看

1
2
3
4
5
6
7
8

Docker拉取了最新镜像,需要4个jar包了

$ ls | grep jar
log4j-1.2.17.jar
slf4j-api-1.7.25.jar
zookeeper-3.6.1.jar
zookeeper-jute-3.6.1.jar

然后使用命令行查看

$ java -classpath :./log4j-1.2.17.jar:slf4j-api-1.7.25.jar:zookeeper-3.6.1.jar:zookeeper-jute-3.6.1.jar org.apache.zookeeper.server.LogFormatter log.1
3.3.14 小结
ls 查看
help查看所有命令
create 路径 数据 -s 代表有序 -e 代临时
get 路径 查询
set 路径 新的数据
delete 路径 单一路径,没有子节点
rmr 路径 递归删除
stat 路径 查看节点状态
日志,依赖4个jar包
3.4 zookeeper的Java Api介绍
3.4.1 ZooKeeper常用Java API
原生Java API

ZooKeeper 原生Java API位于org.apache.ZooKeeper包中

ZooKeeper-3.x.x. Jar 为官方提供的 java API

Apache Curator

Apache Curator是 Apache ZooKeeper的Java客户端库。

Curator.项目的目标是简化ZooKeeper客户端的使用。

ZkClient

开源的ZooKeeper客户端,zkclient-x.x.Jar也是在原生 api 基础之上进行扩展的开源 JAVA 客户端。

3.4.2 创建Java Maven工程,导入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36


4.0.0

org.example
zookeeper
1.0-SNAPSHOT

org.apache.zookeeper zookeeper 3.4.7 org.apache.curator curator-framework 4.0.1 org.apache.curator curator-recipes 4.0.1 junit junit 4.12 3.4.3 CURD节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public class TestZookeeper { //连接地址 String url = "127.0.0.1:2181"; //会话超时时间 int sessionTimeoutMs = 1000; //连接超时时间 int connectionTimeoutMs = 1000; //重试策略 RetryPolicy retryPolicy = null; //创建客户端 CuratorFramework client = null;

String NodeName = null;

@Before
public void createLinkToZK() {
//重试策略
/**
* RetryPolicy: 失败的重试策略的公共接口
* ExponentialBackoffRetry是 公共接口的其中一个实现类
* 参数1: 初始化sleep的时间,用于计算之后的每次重试的sleep时间
* 参数2:最大重试次数
参数3(可以省略):最大sleep时间,如果上述的当前sleep计算出来比这个大,那么sleep用这个时间
/
retryPolicy = new ExponentialBackoffRetry(1000, 1);
//创建客户端
/
*
* 参数1:连接的ip地址和端口号
* 参数2:会话超时时间,单位毫秒
* 参数3:连接超时时间,单位毫秒
* 参数4:失败重试策略
*/
client = CuratorFrameworkFactory.newClient(url, sessionTimeoutMs, connectionTimeoutMs, retryPolicy);
//启动客户端
client.start();
}

@After
public void closeLike() throws Exception {
if (NodeName != null) {
requireNode(NodeName);
} else if (NodeName == null) {
System.out.println(“No Node”);
}
//关闭客户端
client.close();
}

//1. 创建一个空节点(a)(只能创建一层节点)
@Test
public void createOneLevelEmptyNode() throws Exception {
NodeName = “/OneLevelEmptyNode”;
client.create().forPath(NodeName);
}

//2. 创建一个有内容的b节点(只能创建一层节点)
@Test
public void createOneLevelNodeWithMessage() throws Exception {
NodeName = “/OneLevelNodeWithMessage”;
client.create().forPath(NodeName, “OneLevelNodeWithMessage”.getBytes());
}

//3. 创建持久节点,同时创建多层节点
@Test
public void createMultilevelPersistentNode() throws Exception {
NodeName = “/Multilevel/Persistent/Node”;
client.create().creatingParentsIfNeeded().forPath(NodeName, “MultilevelPersistentNode”.getBytes());
}

//4. 创建带有的序号的持久节点
@Test
public void creatPersistentNode() throws Exception {
// NodeName = “/Persistent/Node”;
client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(“/Persistent/Node”, “PERSISTENT_SEQUENTIAL”.getBytes());
}

//5. 创建临时节点(客户端关闭,节点消失),设置延时5秒关闭(Thread.sleep(5000))
@Test
public void creatTempNode() throws Exception {
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(“/Temporary/Node”, “Temporary Node”.getBytes());
}

//6. 创建临时带序号节点(客户端关闭,节点消失),设置延时5秒关闭(Thread.sleep(5000))
@Test
public void creatTempNodeWithSequenceNode() throws Exception {
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(“/TempNode/With/Sequence/Node”, “creatTempNodeWithSequence”.getBytes());
}

//修改节点描述
@Test
public void updateNodeMessage() throws Exception {
NodeName = “/OneLevelNodeWithMessage”;
client.setData().forPath(NodeName, “Update Node Message”.getBytes());
}

//节点数据查询
public void requireNode(String s) throws Exception {
byte[] bytes = client.getData().forPath(String.valueOf(s));
System.out.println(new String(bytes));
}

//删除单个节点
@Test
public void deleteSingleNode() throws Exception {
client.create().forPath(“/testAddForDelete”);
client.delete().forPath(“/testAddForDelete”);
}

//删除多个节点
@Test
public void deleteMultiNode() throws Exception {
client.create().creatingParentsIfNeeded().forPath(“/DeleteMultiNode/deleteMultiNode”, “Create Multi NodeForDelete”.getBytes());
client.delete().deletingChildrenIfNeeded().forPath(“/DeleteMultiNode”);
}

//强制删除
@Test
public void forceDelete() throws Exception {
//NodeName=”/forceDelete/forceDelete”;
client.create().creatingParentsIfNeeded().forPath(“/forceDelete/forceDelete”, “Create Multi NodeForDelete”.getBytes());
client.delete().guaranteed().deletingChildrenIfNeeded().forPath(“/forceDelete”);
}
}
3.4.4 小结
Curator是Appache封装操作Zookeeper的客户端, 操作zookeer数据变得更简单

使用步骤:

创建重试策略

创建客户端 url:port

启动客户端

使用客户端对节点操作

– create forPath, creatingparent…….., withMode(持久,临时)

– setData 修改数据

– getData 查询数据

– delete 删除数据, deletingChildrenIfNeeded递归删除

关闭客户端

3.5 watch机制

3.5.1 什么是watch机制
​ zookeeper作为一款成熟的分布式协调框架,订阅-发布功能是很重要的一个。订阅发布功能,就是观察者模式。观察者会订阅一些感兴趣的主题,然后这些主题一旦变化了,就会自动通知到这些观察者。

zookeeper的订阅发布也就是watch机制,是一个轻量级的设计。因为它采用了一种推拉结合的模式。一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的“推”部分。然后,收到变更通知的客户端需要自己去拉变更的数据,这就是“拉”部分。watche机制分为添加数据和监听节点。

Curator在这方面做了优化,Curator引入了Cache的概念用来实现对ZooKeeper服务器端进行事件监听。Cache是Curator对事件监听的包装,其对事件的监听可以近似看做是一个本地缓存视图和远程ZooKeeper视图的对比过程。而且Curator会自动的再次监听,我们就不需要自己手动的重复监听了。

Curator中的cache共有三种

NodeCache(监听和缓存根节点变化) 只监听单一个节点(变化 添加,修改,删除)
PathChildrenCache(监听和缓存子节点变化) 监听这个节点下的所有子节点(变化 添加,修改,删除)
TreeCache(监听和缓存根节点变化和子节点变化) NodeCache+ PathChildrenCache 监听当前节点及其下的所有子节点的变化
3.5.2 NodeCache
介绍

NodeCache是用来监听节点的数据变化的,当监听的节点的数据发生变化的时候就会回调对应的函数。

增加监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class TestCuratorCache {
//连接地址
String url = “127.0.0.1:2181”;
//会话超时时间
int sessionTimeoutMs = 1000;
//连接超时时间
int connectionTimeoutMs = 1000;
//重试策略
RetryPolicy retryPolicy = null;
//创建客户端
CuratorFramework client = null;

@Before
public void createLinkToZK() {
//重试策略
retryPolicy = new ExponentialBackoffRetry(1000, 1);
//创建客户端
client = CuratorFrameworkFactory.newClient(url, sessionTimeoutMs, connectionTimeoutMs, retryPolicy);
//启动客户端
client.start();
}

@After
public void closeLike() throws Exception {

//关闭客户端
client.close();

}

@Test
public void testNodeCache() throws Exception {
client.create().forPath(“/testNodeCache”);

/*
    *NodeCache(客户端, 被监听节点);
     */
NodeCache nodeCache = new NodeCache(client, "/testNodeCache");
/*
    * true  得到数据
    * false 不能获取节点数据
    */
nodeCache.start(true);

System.out.println(new String(nodeCache.getCurrentData().getData()));

client.setData().forPath("/testNodeCache", "Update Node Cache".getBytes());
nodeCache.getListenable().addListener(new NodeCacheListener() {
  @Override
  public void nodeChanged() throws Exception {
    String data = new String(nodeCache.getCurrentData().getData());
    Thread.sleep(1000 * 2);
    System.out.println(nodeCache.getCurrentData().getPath() + "  \t\t " + data);
  }
});
Thread.sleep(1000 * 4);
client.delete().forPath("/testNodeCache");

}
}
测试

3.5.3 PathChildrenCache
介绍

PathChildrenCache是用来监听指定节点 的子节点变化情况

增加监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Test
public void testPathChildrenCache() throws Exception {

client.create().forPath(“/testPathChildrenCache”);
/*
* new PathChildrenCache(客户端, 节点 , 是/否得到数据);
/
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, “/testPathChildrenCache”, true);
///
*
// * NORMAL: 普通启动方式, 在启动时缓存子节点数据
// * POST_INITIALIZED_EVENT:在启动时缓存子节点数据,提示初始化
// * BUILD_INITIAL_CACHE: 在启动时什么都不会输出
// * 在官方解释中说是因为这种模式会在start执行执行之前先执行rebuild的方法,而rebuild的方法不会发出任何事件通知。
// */
pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);

System.out.println(pathChildrenCache.getCurrentData());

client.create().forPath(“/testPathChildrenCache/A”);
client.setData().forPath(“/testPathChildrenCache/A”, “Update Node Message”.getBytes());
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED) {

    System.out.println("CHILD_UPDATED Node: "+pathChildrenCacheEvent.getData().getPath());
    System.out.println("CHILD_UPDATED Node Date :"+new String(pathChildrenCacheEvent.getData().getPath()));
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.INITIALIZED) {
    System.out.println("Initialization");
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
    System.out.println("CHILD_REMOVED Node: "+pathChildrenCacheEvent.getData().getPath());
    System.out.println("CHILD_REMOVED Node Date :"+new String(pathChildrenCacheEvent.getData().getPath()));
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED) {
    System.out.println("CHILD_ADDED Node: "+pathChildrenCacheEvent.getData().getPath());
    System.out.println("CHILD_ADDED Node Date :"+new String(pathChildrenCacheEvent.getData().getPath()));
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED) {
    System.out.println("CONNECTION_SUSPENDED");
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED) {
    System.out.println("CONNECTION_RECONNECTED");
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CONNECTION_LOST) {
    System.out.println("CONNECTION_LOST");
  }
}

});
client.delete().guaranteed().deletingChildrenIfNeeded().forPath(“/testPathChildrenCache”);
}
3.5.4 TreeCache
介绍

TreeCache有点像上面两种Cache的结合体,NodeCache能够监听自身节点的数据变化(或者是创建该节点),PathChildrenCache能够监听自身节点下的子节点的变化,而TreeCache既能够监听自身节点的变化、也能够监听子节点的变化。

添加监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Test
public void testTreeCache() throws Exception {

client.create().forPath(“/testTreeCache”);
//监听当前Node Data Tree
TreeCache treeCache = new TreeCache(client, “/testTreeCache”);
treeCache.start();

System.out.println(treeCache.getCurrentData(“/testTreeCache”));

treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
if (treeCacheEvent.getType() == TreeCacheEvent.Type.NODE_ADDED) {
System.out.println(“NODE_ADDED: “+treeCacheEvent.getData().getPath());

    client.delete().deletingChildrenIfNeeded().forPath("/testTreeCache/Test");

  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.NODE_REMOVED) {
    System.out.println("NODE_REMOVED: "+treeCacheEvent.getData().getPath());

  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.NODE_UPDATED) {
    System.out.println("NODE_UPDATED: "+treeCacheEvent.getData().getPath());

  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.INITIALIZED) {
    System.out.println("INITIALIZED");
  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.CONNECTION_SUSPENDED) {
    System.out.println("CONNECTION_SUSPENDED");
  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.CONNECTION_RECONNECTED) {
    System.out.println("CONNECTION_RECONNECTED");
  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.CONNECTION_LOST) {
    System.out.println("CONNECTION_LOST");
  }
}

});
client.delete().guaranteed().deletingChildrenIfNeeded().forPath(“/testTreeCache”);
}
3.5.5 小结
Zookeeper:分布小文件存储系统,树形层级数据结构,路径唯一

动物管理员,管的是服务
应用:注册中心、配置中心、分布式锁、分布式队列、负载均衡
配置zoo.cfg dataPath, 2181

节点类型:持久化(有序与无序 -s),临时(-e 临时, -s 有序与无序) crud

api: Curator 创建重试策略->客户端(url ip:port)->启动->使用client操作crud->关闭

Watch: 监听节点数据变化,一旦生变更(添加、修改、删除)时,服务端通知客户端(addListener)

NodeCache: 听监听单一节点变化

PathChildrenCache: 监听节点下的子节点
TreeCache: NodeCache+PathChildrenCache

四.zookeeper集群搭建
目标

搭建Zookeeper集群

操作路径

了解集群

集群介绍
集群模式
集群原理及架构图

搭建集群(使用linux)

集群中Leader选举机制
测试集群

4.1 了解集群
4.1.1 集群介绍
Zookeeper 集群搭建指的是 ZooKeeper 分布式模式安装。 通常由 2n+1台 servers 组成 奇数。 这是因为为了保证 Leader 选举(基于 Paxos 算法的实现) 能或得到多数的支持,所以 ZooKeeper 集群的数量一般为奇数。

4.1.2 集群模式
伪分布式集群搭建
完全分布式集群搭建
4.2 架构图

集群工作的核心
事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性;
集群内部各个服务器的调度者。
对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。

Leader:Zookeeper(领导者):

集群工作的核心
事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性;
集群内部各个服务器的调度者。
对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。

Follower(跟随者)

处理客户端非事务(读操作) 请求,转发事务请求给 Leader;参与集群 Leader 选举投票 2n-1台可以做集群投票。

Observer:观察者角色

针对访问量比较大的 zookeeper 集群, 还可新增观察者角色。观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。
不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

4.3 搭建过程
4.3.1 配置zookeeper-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: ‘2’
services:
zoo1:
image: zookeeper:3.4.14
restart: always
container_name: zoo1
ports:
- “2181:2181”
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

zoo2:
    image: zookeeper:3.4.14
    restart: always
    container_name: zoo2
    ports:
        - "2182:2181"
    environment:
        ZOO_MY_ID: 2
        ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
 
zoo3:
    image: zookeeper:3.4.14
    restart: always
    container_name: zoo3
    ports:
        - "2183:2181"
    environment:
        ZOO_MY_ID: 3
        ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

这个配置文件会告诉 Docker 分别运行三个 zookeeper 镜像, 并分别将本地的 2181, 2182, 2183 端口绑定到对应的容器的2181端口上.
ZOO_MY_ID 和 ZOO_SERVERS 是搭建 ZK 集群需要设置的两个环境变量, 其中 ZOO_MY_ID 表示 ZK 服务的 id, 它是1-255 之间的整数, 必须在集群中唯一. ZOO_SERVERS 是ZK 集群的主机列表.

4.3.2 启动zookeeper
1
2
3
4
5
$ docker-compose -f zookeeper-compose.yml up -d
Creating network “program_default” with the default driver
Creating zoo3 … done
Creating zoo2 … done
Creating zoo1 … done
4.3.3 查看zookeeper状态
1
2
3
4
5
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
63dacd015bf9 zookeeper:3.4.14 “/docker-entrypoint.…” 6 minutes ago Up 6 minutes 2888/tcp, 0.0.0.0:2181->2181/tcp, 3888/tcp zoo1
2bfaa57664f9 zookeeper:3.4.14 “/docker-entrypoint.…” 6 minutes ago Up 6 minutes 2888/tcp, 3888/tcp, 0.0.0.0:2182->2181/tcp zoo2
4ff61abb0d2f zookeeper:3.4.14 “/docker-entrypoint.…” 6 minutes ago Up 6 minutes 2888/tcp, 3888/tcp, 0.0.0.0:2183->2181/tcp zoo3
4.4 leader选举
在领导者选举的过程中,如果某台ZooKeeper获得了超过半数的选票,则此ZooKeeper就可以成为Leader了。

服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。

服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,

每个Server发出一个投票由于是初始情况,1和2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时1的投票为(1, 0),2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
接受来自各个服务器的投票集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器

处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下

    · 优先检查ZXID。ZXID比较大的服务器优先作为Leader。

    · 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器

由于服务器2的编号大,更新自己的投票为(2, 0),然后重新投票,对于2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可,此时集群节点状态为LOOKING。

统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息

服务器3启动,进行统计后,判断是否已经有过半机器接受到相同的投票信息,对于1、2、3而言,已统计出集群中已经有3台机器接受了(3, 0)的投票信息,此时投票数正好大于半数,便认为已经选出了Leader,

改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING

所以服务器3成为领导者,服务器1,2成为从节点。

4.5 测试集群
测试使用任何一个IP都可以获取

测试如果有个机器宕机,(./zkServer.sh stop),会重新选取领导者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class TestZookeeperCluster {

CuratorFramework client = null;

@After
public void closeLike() throws Exception {
    //关闭客户端
    client.close();
}

@Test
public void createNode() throws Exception {
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 3000, 3000, retryPolicy);
    client.start();
    client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/createNode/Nodes");
}

@Test
public void updateNode() throws Exception {
    //创建失败策略对象
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    client = CuratorFrameworkFactory.newClient("127.0.0.1:2182", 1000, 1000, retryPolicy);
    client.start();
    client.setData().forPath("/createNode/Nodes", "Update Node".getBytes());
    client.close();
}

@Test
public void getData() throws Exception {
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    client = CuratorFrameworkFactory.newClient("127.0.0.1:2183", 1000, 1000, retryPolicy);
    client.start();
    byte[] bytes = client.getData().forPath("/createNode/Nodes");
    System.out.println(new String(bytes));
    client.close();
}

@Test
public void deleteNode() throws Exception {
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    client = CuratorFrameworkFactory.newClient("127.0.0.1:2183", 1000, 1000, retryPolicy);
    client.start();
    client.delete().deletingChildrenIfNeeded().forPath("/createNode");
    client.close();
}

}
4.6 小结
什么是集群:把具有相同功能应用布署多个到网络中,聚集一起,实现数据的同步,一旦有应用服务挂了,集群中的其它应用服务可以顶上,保证应用的正常运作

Zookeeper集群架构

Leader领导者 可以进行读写,写操作成功后会同步到各个从和观察者

Follower跟从者 只能读,如果有写操作时则转给Leader领导者处理 参与选举

Observer观察者 只能读,有写时也会转给Leader, 不参与Leader的选举

集群服务器个要为奇数2n+1,防止服务器down机,可以选举出新的leader

Leader先举机制

先投自己一票,票数相同时 票数>zxid>myid,其它zookeeper改为大者一票。集群中半数以上的票数才能成Leader

一.Maven
1.1 Maven基础回顾
目标

回顾Maven基础课程

学习路径

Maven好处
安装配置Maven
三种仓库
常见命令
坐标的书写规范
如何添加坐标
依赖范围
1.1.1 回顾
什么是Maven

Maven 是专门用于构建和管理Java相关项目的工具。

Maven好处

节省磁盘空间
可以一键构建
可以跨平台
应用在大型项目时可以提高开发效率(jar包管理,maven的工程分解,可分模块开发)
安装配置 maven

三种仓库

本地仓库,远程仓库(私服), 公司内部中央仓库

常见的命令

Compile
Test
Package(jar,war)
Install(安装到本地仓库)
Deploy(部署到远程仓库(私服))
Clean(target下生成的文件)
坐标的书写规范

groupId 公司或组织域名的倒序
artifactId 项目名或模块名
version 版本号
如何添加坐标

在本地仓库中搜索
互联网上搜,推荐网址 http://www.mvnrepository.com/
依赖范围

属性:

依赖范围 对于编译classpath有效 对于测试classpath有效 对于运行classpath有效 例子
Compile(默认) Y Y Y Spring-core
Test - Y - Junit
Provided Y Y - servlet-api
Runtime - Y Y JDBC驱动
System Y Y - 本地的,Maven仓库之外的类库
1.1.2 小结
Maven好处
安装配置Maven(熟练)
三种仓库(本地仓库(阿里镜像),中央仓库(远程仓库),私服(代理仓库))
常见命令(clean compile test package install deploy)
坐标的书写规范(groupId artifactId version)
如何添加坐标(http://www.mvnrepository.com/)
依赖范围
私服的配置(settings.xml配置私服 工作中大概率要配置的)
1.2 Maven中jar包冲突问题
目标

使用maven依赖导入jar包,出现相同名称但版本不一致的jar包

学习路径

演示jar包冲突问题:遵循第一声明者优先原则
解决jar包冲突问题,路径近者优先
解决jar包冲突问题,直接排除法
首先创建一个Mavenjar包工程(New Project-Maven)

1.2.1 第一声明优先原则
哪个jar包在靠上的位置,这个jar包就是先声明的,先声明的jar包下的依赖包,可以优先引入项目中。

在pom.xml中引入如下坐标,分别是spring中不同的版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28


4.0.0

com.test
Maven_Advanced
1.0-SNAPSHOT

jar

org.springframework spring-context 5.0.2.RELEASE
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>4.2.4.RELEASE</version>
</dependency>
在控制面板的maven面板,点击查看依赖关系按钮,看到了包和包之间的依赖关系存在冲突,都使用了spring-core包,关系图如下:

看看他们依赖包的导入,发现导入的包却没有问题,包使用的都是5.0.2的版本。

我们把上面2个包的顺序调换后就变成了低版本的依赖导入。

1.2.2 路径近者优先原则
直接依赖:项目中直接导入的jar包就是项目的直接依赖包。
传递依赖(间接依赖):项目中没有直接导入的jar包,可以通过中直接依赖包传递到项目中去。
直接依赖比传递依赖路径近,最终进入项目的jar包会是路径近的直接依赖包。
修改jar包,直接引入依赖spring-core

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

org.springframework spring-beans 4.2.4.RELEASE
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<!--引入直接依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.2.8.RELEASE</version>
</dependency>
此时优先引入的是直接依赖的引用

1.2.3 直接排除法
问题:整合项目需要用到5的版本,引入4的版本,会不会出现异常(类没有找到,方法没有找到)
当我们需要排除某个jar包的依赖时,在配置exclusions标签的时候,内部可以不写版本号。pom.xml依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

org.springframework spring-beans 4.2.4.RELEASE org.springframework spring-core
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
解决方案

pom.xml添加exclusions

1
2
3
4
5
6


spring-core
org.springframework


1.2.4 小结
出现1个项目存在多个同种jar包的时候,需要我们进行解决maven的jar包冲突问题(异常:Class not found、Method not found等)

路径近者
节点路径相同时,使用第一声明优先(xml上下顺序有关)
无法通过依赖管理原则排除的,使用直接排除法
1.3 工程分层
之前项目开发:

工程分层后的开发:所有的service和dao的代码都在一起,1:增强程序的通用性,2:降低了代码的耦合性

目标

实现项目工程分解,掌握聚合和继承的作用

学习路径

聚合和继承
准备数据库环境
ssm_parent(父工程) pom 引入依赖
ssm_model(子工程) 实体类
ssm_dao(子工程) 数据库操作
ssm_service(子工程) 完业务操作
ssm_web(子工程) 收集请求参数,调用业务,返回结果给前端
测试(工程发布tomcat)
1.3.1 聚合(多模块)和继承
继承和聚合结构图:

特点1(继承)

ssm_parent父工程:存放项目的所有jar包。

ssm_web和ssm_service和ssm_dao有选择的继承jar包,并在工程中使用。这样可以消除jar包重复,并锁定版本

特点2(聚合)

ssm_web依赖于ssm_service,ssm_service依赖于ssm_dao,启动ssm_web,便可访问程序。

执行安装的时候,执行ssm_parent,就可以将所有的子工程全部进行安装。

理解继承和聚合总结

通常继承和聚合同时使用。

何为继承

继承是为了消除重复,如果将 dao、 service、 web 分开创建独立的工程则每个工程的 pom.xml 文件中的内容存在重复,比如:设置编译版本、锁定 spring 的版本的等,可以将这些重复的 配置提取出来在父工程的 pom.xml 中定义。

何为聚合

项目开发通常是分组分模块开发, 每个模块开发完成要运行整个工程需要将每个模块聚合在 一起运行,比如: dao、 service、 web 三个工程最终会打一个独立的 war 运行。

1.3.2 准备数据库环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SET FOREIGN_KEY_CHECKS=0;

CREATE DATABASE Maven;
use Maven;
DROP TABLE IF EXISTS items;
CREATE TABLE items (
id int(10) NOT NULL auto_increment,
name varchar(20) default NULL,
price float(10,0) default NULL,
pic varchar(40) default NULL,
createTime datetime default NULL,
detail varchar(200) default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

INSERT INTO items VALUES (‘1’, ‘Test01’, ‘1000’, null, ‘2018-03-13 09:29:30’, ‘带我走上人生巅峰’);
INSERT INTO items VALUES (‘2’, ‘Test02’, null, null, ‘2018-03-28 10:05:52’, ‘插入测试’);
INSERT INTO items VALUES (‘3’, ‘Test03’, ‘199’, null, ‘2018-03-07 10:08:04’, ‘插入测试’);
INSERT INTO items VALUES (‘7’, ‘Test04’, null, null, null, null);
INSERT INTO items VALUES (‘8’, ‘Test05’, null, null, null, null);
1.3.3 ssm_parent
创建ssm_parent的Maven Project
删除src文件夹
1.3.3.1 pom.xml
父工程:pom

详细信息
小结

pom
管理所有的jar包(并锁定版本)
父工程不需要开发的代码,src的文件夹可以删除
1.3.4 ssm_model
选择ssm_parent=>New->Moudle->Maven->创建子项目ssm_model(即实体类)

1.3.4.1 pom.xml
查看子工程pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15



ssm_parent
com.test
1.0-SNAPSHOT

4.0.0

<artifactId>ssm_model</artifactId>
<packaging>jar</packaging>
回去查看ssm_parent的pom.xml,自动聚合类ssm_model

1
2
3

ssm_model

1.3.4.2 创建Items.java
1
2
3
4
5
6
7
8
9
public class Items {
private Integer id;
private String name;
private Float price;
private String pic;
private Date createTime;
private String detail;
//此处省略getter/setter,toString
}
小结
jar
管理项目中所有的实体类
1.3.5 ssm_dao
操作路径
配置pom.xml
创建spring-mybatis.xml(spring整合mybatis的配置)
创建ItemsDao.java
创建ItemsDao.xml
1.3.5.1 pom.xml
详细信息
查看父工程pom.xml

1
2
3
4

ssm_model
ssm_dao

1.3.5.2 创建spring-mybatis.xml
操作路径

设置数据库连接池
配置SqlSessionFactoryBean
添加Dao层接口扫描,让Dao被spring管理
spring-mybatis.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25








<!--2.配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--配置数据源-->
    <property name="dataSource" ref="dataSource"/>
    <!--别名配置-->
    <property name="typeAliases" value="com.test.domain"/>
</bean>

<!--3.dao接口扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.test.dao"/>
</bean>
1.3.5.3 创建ItemsDao.java 1 2 3 4 5 public interface ItemsDao { List findAll();
int save(Items items);

}
1.3.5.4 创建ItemsDao.xml
com/test/dao/ItemsDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

INSERT INTO items(name,price,pic,creaTetime,detail) VALUES(#{name},#{price},#{pic},#{createTime},#{detail}) 1.3.5.5 dao测试 1 2 3 4 5 6 7 8 9 10 @Test public void testFindAll() { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:spring-mybatis.xml"); ItemsDao itemsDao = classPathXmlApplicationContext.getBean(ItemsDao.class); List all = itemsDao.findAll();

for (Items items : all) {
System.out.println(items);
}
}
小结
jar
引入依赖 ssm_model 、mybatis的依赖…
spring-mybatis.xml(spring的配置文件)(spring整合mybatis的框架)
数据源
工厂
包扫描(dao)
接口ItemsDao.java
创建映射文件ItemsDao.xml (映射文件路径与包名相同, resources目录下的)
1.3.6 ssm_service
操作路径
pom.xml
创建spring-service.xml(spring的声明式事务处理)
创建ItemsService接口
创建ItemsServiceImpl实现类
1.3.6.1 pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49



ssm_parent
com.test
1.0-SNAPSHOT

4.0.0

<artifactId>ssm_service</artifactId>


<!--jar包-->
<packaging>jar</packaging>

<!--依赖-->
<dependencies>
    <!--依赖dao-->
    <dependency>
        <groupId>com.test</groupId>
        <artifactId>ssm_dao</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- spring -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>
</dependencies>
1.3.6.2 创建spring-service.xml 操作路径 创建一个事务管理器 配置事务的通知,及传播特性,对切入点方法的细化 AOP声明式事务配置(配置切入点,通知关联切入点) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
<!--2.方式一:声明式事务配置-->
<tx:advice id="txAdvice" transaction-manager="txtManager">
    <!--配置传播特性属性-->
    <tx:attributes>
        <!--
            对应方法参与事务并且在事务下执行,事务隔离剂别使用默认隔离级别,发生异常需要事务回滚
        -->
      <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="edit*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
        <!--
            只读操作
        -->
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>
 <!--AOP声明式事务配置(配置切入点,通知关联切入点)-->
<aop:config>
    <!--切点指点-->
    <aop:pointcut id="tranpointcut" expression="execution(* com.test.service.*.*(..))" />
    <!--配置通知-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="tranpointcut" />
</aop:config>
<!--方式二:注解方式事务配置-->
<!--<tx:annotation-driven transaction-manager="txtManager"/>-->
<!--3.扫描service-->
<context:component-scan base-package="com.test.service"/>
<!--4.引入spring-mybatis.xml-->
<import resource="spring-mybatis.xml" />
1.3.6.3 创建ItemsService接口 1 2 3 4 5 public interface ItemsService { List findAll();
void save(Items items);

}
1.3.6.4 创建ItemsServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class ItemsServiceImpl implements ItemsService {
@Autowired
private ItemsDao itemsDao;

@Override
public List<Items> findAll() {
    return itemsDao.findAll();
}

@Override
public void save(Items items) {
    itemsDao.save(items);
}

}
1.3.6.5 service测试
1
2
3
4
5
6
7
8
9
10
11
12
public class ServiceTest {
@Test
public void testService(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(“classpath:spring-service.xml”);
ItemsService bean = classPathXmlApplicationContext.getBean(ItemsService.class);
List all = bean.findAll();

for (Items items : all) {
  System.out.println(items);
}

}
}
小结
jar
引入依赖 ssm_dao 、spring相关的依赖
spring-service.xml(声明式事务处理)
扫描业务层
事务管理器
通知->切的方法
aop:config
要切的包 表达式
把advice与pointcut整合起来
注解式事务
接口ItemsService.java
实现类ItemsServiceImpl.java
1.3.7 ssm_web
操作路径
pom.xml
创建web.xml
创建springmvc.xml
创建ItemsController.java
创建页面items.jsp
1.3.7.1 pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78



ssm_parent
com.test
1.0-SNAPSHOT

4.0.0

ssm_web

war

com.test ssm_service 1.0-SNAPSHOT
<!--导入springmvc-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
</dependency>

<!--servletAPI -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <scope>provided</scope>
</dependency>

<!--jstl表达式 -->
<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
</dependency>
org.apache.tomcat.maven tomcat7-maven-plugin 18081 / UTF-8 1.3.7.2 创建web.xml 操作路径

配置编码过滤器

springmvc前端核心控制器

1
2
3
4
5
6
7
8
9
10
11
12
.
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
│ └── java
└── ssm_web.iml
web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37




encodingFilter
org.springframework.web.filter.CharacterEncodingFilter


encoding
UTF-8



encodingFilter
/*

<!--2.springmvc核心控制器-->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!--启动优先级-->
    <load-on-startup>1</load-on-startup>
</servlet>
<!--3.指定映射拦截-->
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
1.3.7.3 创建springmvc.xml 操作路径

包扫描
视图解析器
springmvc注解驱动,自动配置mvc的处理器适配器和处理映射器
静态资源不过滤
mport导入spring.xml
springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23



<context:component-scan base-package=”com.test.controller”/>






mvc:annotation-driven/

mvc:default-servlet-handler/



1.3.7.4 创建ItemsController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

@Controller
@RequestMapping(“/list”)
public class ItemsController {

@Autowired
private ItemsService itemsService;

@GetMapping("/findAll")
public String findAll(Model model){
    List<Items> list = itemsService.findAll();
    for (Items items : list) {
        System.out.println(items);
    }
    model.addAttribute("list",list);
    return "list";
}

@PostMapping("/save")
public String  save(Items items){
    itemsService.save(items);
    return "redirect:/list/findAll";
}

}
1.3.7.5 创建页面items.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core“ %>
<%@ page contentType=”text/html;charset=UTF-8” language=”java” %>

Title
名称
价格
创建日期
详情

ID name price createTime detail
${item.id} ${item.name} ${item.price} ${item.pic} ${item.createTime} ${item.detail}
1.3.7.6 小结 war 引入依赖 ssm_service 、 springmvc 、 servlet_api等 springmvc.xml(springmvc的相关配置) web.xml(springmvc的核心控制器、post乱码过滤器) 类ItemsController.java 测试页面items.jsp 1.3.8 测试 操作路径 测试打包package、安装install(演示maven聚合) 发布到外部tomcat 使用maven的tomcat插件发布 1.3.8.1 方式一:tomcat插件发布(配置父工程) parent是有聚合的功能,不需要将ssm_parent,ssm_model,ssm_dao,ssm_service安装到本地仓库。

1.3.8.2 方式二:tomcat插件发布(配置web工程)
需要将ssm_parent,ssm_model,ssm_dao,ssm_service安装到本地仓库。

1.3.8.3 方式三:发布到外部tomcat
当使用外部tomcat进行开发的时候,不需要将ssm_parent,ssm_model,ssm_dao,ssm_service安装到本地仓库。

1.3.8.4 小结
1:拆分与聚合和继承

​ 拆分:解耦,代码最大程度的重复使用,维护方便

​ 聚合:靠父工程来聚合,单一工程是没法完成工作。

​ 继承:父工程引入依赖,子工程就不需要再引了

2: Maven的jar包冲突解决

路径近者优先
路径相同,第一声明优先
路径相同,使用强制排除exclusions
3:准备数据库环境

ssm_parent(父工程)聚合,引入依赖管理

ssm_model(子工程) 实体类

ssm_dao(子工程) mybatis, dataSource, sqlSessionFactorybean, MapperScannerConfigurer

注:映射文件的路径必须与包名路径一样

ssm_service(子工程)

扫service包,事务管理器,通知、切面。

ssm_web(子工程)

SpringMVC.xml 扫controller, 视图解析器,注解驱动,静态资源过滤
web.xml 编码过滤器,前端核心控制器(load-on-startup)
8:测试(工程发布tomcat)

推荐使用本地tomcat
1.4 补充
Maven项目,如果正在下载一个jar,但是突然断网,此时,就会产生一个m2e-lastUpdated.,别指望下次上网就会自动下载,必须手动删除该文件,然后再进行下载。

二.Git
学习目标:

能够概述git基本概念
能够概述git工作流程
能够使用git基本命令
能够使用idea操作git
2.1 Git版本控制系统目标
速度
简单的设计
对非线性开发模式的强力支持(允许上千个并行开发的分支)
完全分布式
有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
2.2 Git与svn对比
SVN是集中式版本控制系统,版本库是集中放在中央服务器的

集中管理方式在一定程度上看到其他开发人员在干什么,而管理员也可以很轻松掌握每个人的开发权限。

但是相较于其优点而言,集中式版本控制工具缺点很明显:

服务器单点故障
容错性差
Git是分布式版本控制系统,那么它就没有中央服务器的

小结

git: 是一个代码(文档)版本的管理工具

svn:集中式版本控制工具(服务器完成对文件的版本控制)(一个仓库)

git:分布式版本控制工具(客户端、服务器都可以完成对文件的版本控制)(两个仓库,本地仓库、远程仓库)

2.3 git工作流程
一般工作流程如下:

从远程仓库中克隆 Git 资源作为本地仓库。

从本地仓库中checkout代码然后进行代码修改

在提交前先将代码提交到暂存区。

提交执行commit命令。提交到本地仓库。本地仓库中保存修改的各个历史版本。

在修改完成后,需要和团队成员共享代码时,可以将本地仓库的代码push到远程仓库。

小结

本地仓库——存入工程代码zip
工作区—— idea工作目录(代码明文)
暂存区
​ 命令:

远程仓库:(代码与他人共享的地方)

add(添加到暂存区);

commit(提交到本地仓库)

clone(远程仓库的代码克隆到本地仓库 所有的版本);

pull(将代码从远程仓库拉取到本地开发 只拉取某一版本的代码, 会尝试合并(远程与本地));

push(将代码从本地仓库推送到远程仓库)

clone与pull的区别

​ clone:第一次连接远程仓库 所有的版本 初始化

​ pull:第一次用clone命令,如果已经连接是哪个,其他都用pull命令拉取 只拉取某一版本的代码 会尝试合并(远程与本地)

2.4 使用git管理文件版本
目标

使用git在本地仓库完成版本控制,及相关命令的是使用

学习路径

创建版本库
添加/修改/删除文件/删除文件并保留副本
将java工程提交到版本库
忽略文件(提交版本库时,可忽略某些文件)
2.4.1 创建版本库
什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的新增、修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。由于git是分布式版本管理工具,所以git在不需要联网的情况下也具有完整的版本管理能力。

创建本地仓库执行命令

1
$ git init
查看

1
2
$ ls -a | grep git
.git
创建服务器仓库

1
$ git –bare init
小结

git init 有工作空间,可以编码,一般用于客户端

git –bare init没有有工作空间,不可以编码,一般用于服务端

概念:

版本库

“.git”目录就是版本库,将来文件都需要保存到版本库中。

工作目录(工作区)

包含“.git”目录的目录,也就是.git目录的上一级目录就是工作目录。只有工作目录中的文件或者是文件夹才能保存到版本库中。

2.4.2 添加文件
2.4.2.1 添加文件过程
在git init的目录下创建一个文件

1
$ touch readme.txt
使用git add 命令即加入缓存区

1
$ git add readme.txt
使用git commit 添加注释并提交

1
$ git commit -m “Comment Message”
2.4.2.2 工作区和暂存区
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

什么是工作区(Working Directory)?

​ 工作区就是你在电脑里能看到的目录,比如我的reporstory/repo1文件夹就是一个工作区。

什么是版本库?

​ 在这个工作区目录中的“.git”隐藏文件夹是版本库。

​ Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

2.4.2.3 修改文件
2.4.2.3.1 提交修改
修改文件

显示修改文件

1
$ git status
显示修改内容

1
$ git diff readme.txt
使用git add 命令即加入缓存区

1
$ git add readme.txt
使用git commit 添加注释并提交

1
$ git commit -m “New Comment Message”
2.4.2.3.2 查看修改历史
查看修改历史

1
2
3
4
5
6
7
8
9
10
$ git log | less | grep -v “swzxsyh”
commit 360abaca1a57aa9a9652c57fc3b06cf8a2c16742
Date: Tue Jan 29 17:01:39 2019 +0800

Learning Part of C Language

commit 9b1e91b72926493818ccbafd1447c2ae3fe8af55
Date: Tue Jan 29 16:57:04 2019 +0800

Cisco Packet Tracer ReadME
恢复版本

1
$ git checkout –
2.4.2.3.3 差异比较
1
$ git diff 新版本号 旧版本号
2.4.2.3.4 还原修改
1
$ git reset HEAD
注意:此操作会撤销所有未提交的修改,所以当使用还原操作是需要慎重

2.4.2.4 删除文件
需要删除无用的文件时可以使用git提供的删除功能直接将文件从版本库中删除。

1
2
3
4
5
6
7
$ rm readme.txt

$ git rm readme.txt

$ git commit -m “rm readme.txt”

$ git push resp
2.4.2.5 删除文件并保留副本
1
2
3
4
#对于一个文件:
git rm –cached mylogfile.log
#对于一个目录:
git rm –cached -r mydirectory
2.4.2.6 案例:将java工程-提交到版本库
创建HelloProjet项目,将工程添加到暂存区。

1
$ git add HelloProjet/
忽略文件或文件夹

并不是所有文件都需要保存到版本库中的例如“out”目录及目录下的文件就可以忽略。

需要创建.gitignore文件,并添加ignore内容

1
$ echo “out.idea” >> .gitignore
提交代码

1
$ git commit -m “HelloProjet”
2.4.2.7 忽略文件(.gitignore)语法规范
空行或是以 # 开头的行即注释行将被忽略。

可以在前面添加正斜杠 / 来避免递归,下面的例子中可以很明白的看出来与下一条的区别。

可以在后面添加正斜杠 / 来忽略文件夹,例如 build/ 即忽略build文件夹。

可以使用 ! 来否定忽略,即比如在前面用了 *.apk ,然后使用 !a.apk ,则这个a.apk不会被忽略。

*用来匹配零个或多个字符,如 *.[oa] 忽略所有以”.o”或”.a”结尾, *~ 忽略所有以 ~ 结尾的文件(这种文件通常被许多编辑器标记为临时文件); [] 用来匹配括号内的任一字符,如 [abc] ,也可以在括号内加连接符,如 [0-9] 匹配0至9的数; ? 用来匹配单个字符。

看了这么多,还是应该来个例子:

gitignore文件

1
2
3
4
5
6
7
8
9
10
11
12

忽略 .a 文件

*.a

不忽略 lib.a, 尽管已经在前面忽略了 .a 文件

!lib.a

仅在当前目录下忽略 TODO 文件, 但不包括子目录下的 subdir/TODO

/TODO

忽略 build/ 文件夹下的所有文件

build/

忽略 doc/notes.txt, 不包括 doc/server/arch.txt

doc/*.txt

忽略所有的 .pdf 文件,包括在 doc/ directory 下的

doc/**/*.pdf
2.4.2.8 小结
创建版本库

git init:初始化仓库(包含工作目录) –bare[创建的是纯仓库]

添加文件

git add:把文件添加进暂存区

git commit提交文件至本地仓库 添加提交日志

修改文件

删除文件

删除文件并保留副本

忽略文件(提交版本库时,可忽略某些文件。提交工程源码与jar和pom.xml,其它不提交)

将java工程提交到版本库【重点-不要提交忽略文件】

2.5 远程仓库
目标

使用git在远程仓库完成版本控制,及相关命令的使用,远程仓库可实现项目组人员之间的文件版本控制

学习路径

添加到远程仓库

(1)在github上创建仓库

(2)什么是ssh协议

(3)使用ssh协议同步到远程仓库

(4)使用https协议同步到远程仓库

从远程仓库上克隆

从远程仓库取代码

解决多人协作中版本冲突问题

搭建私有Git服务器(linux环境)

2.5.1 添加远程仓库
此时在本地已有一个Git仓库,想让其他人来协作开发,此时可以把本地仓库同步到远程仓库,同时还增加了本地仓库的一个备份。

2.5.1.1 在github上创建仓库
官网:https://github.com/

注册账号->登录->新建仓库->Start a project->->点击“create repository”按钮仓库即可

Github支持两种同步方式“https”和“ssh”。

使用https很简单基本不需要配置就可以使用,但是每次提交代码和下载代码时都需要输入用户名和密码

2.5.1.1.2 ssh协议版本
SSH 为 Secure Shell的缩写,利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。

使用ssh方式就需要客户端先生成一个密钥对,即一个公钥一个私钥。然后还需要把公钥放到githib的服务器上。

Client

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# ssh-keygen -t rsa <== 建立密钥对,-t代表类型,有RSA和DSA两种
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): #密钥文件默认存放位置,按Enter即可
Created directory ‘/root/.ssh’.
Enter passphrase (empty for no passphrase): #输入密钥锁码
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa. #生成的私钥
Your public key has been saved in /root/.ssh/id_rsa.pub. #生成的公钥
The key fingerprint is:
SHA256:
ssh密钥配置

密钥生成后需要在github上配置密钥本地才可以顺利访问。

在key部分将id_rsa.pub文件内容添加进去,然后点击“Add SSH key”按钮完成配置。

使用ssh协议同步到远程仓库

1
2
$ git remote add origin git@github.com:itcast888/mytest.git
$ git push -u origin master
origin就是一个名字,它是在你clone一个托管在Github上代码库时,git为你默认创建的指向这个远程代码库的标签, origin指向的是repository,master只是这个repository中默认创建的第一个分支。

2.5.2 从远程仓库克隆
克隆远程仓库也就是从远程把仓库复制一份到本地,克隆后会创建一个新的本地仓库。选择一个任意部署仓库的目录,然后克隆远程仓库。

1
$ git clone git@github.com:sublun/mytest.git
2.5.3 从远程仓库取代码
1
2
3

  1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge(合并代码)

  2. git pull:相当于是从远程获取最新版本并merge到本地,上述命令其实相当于git fetch 和 git merge
    在实际使用中,git fetch更安全一些,但是不常用!

因为在merge前,我们可以查看更新情况,然后再决定是否合并。

git pull更常用,因为即得代码又可以自动合并

2.5.4 解决版本冲突
原因

同时获取同一份代码,用户A先提交,用户B再提交,即出现版本冲突

解决

先拉取(pull)远程仓库的代码到本地。

编辑冲突

合并成一个新的文件

再次推送

避免冲突

优先提交代码
提交后通知全组成员pull
2.6 分支管理
目标

在本地仓库中使用分支,可在一个项目中使用多条路径,完成版本控制。

学习路径

分支的概念

分支管理

(1)创建分支(2)合并分支(3)解决冲突(4)删除分支

2.6.1 分支的概念
在我们每次的提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD指针严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。

只要有本地仓库就有master分支。

1
2
3
4
5
6
$ git checkout
A “Algorithm/\347\256\227\346\263\225\345\233\276\350\247\243/.idea/$CACHE_FILE$”
M C++ project/.vscode/launch.json
M C++ project/.vscode/tasks.json
M Java/MyFirstApp.class
Your branch is up to date with ‘origin/master’.
Master是条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

1
2
3
Head

A–>B–>C–>D–>E(Master)
创建新的分支,如dev,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,此时,对工作区的修改和提交就针对dev分支,提交后,dev指针往前移动一步,而master指针不变

1
2
$ git checkout -b dev
Switched to a new branch “dev”
1
2
3
4
5
6
7
Master

A–>B–>C–>D–>E

dev

Head
dev上的工作完成了,就可以把dev合并到master上,删除dev分支,变回一条线

1
2
3
4
5
6
7
8
9
10
#在切换分支之前,保持好一个干净的状态
$ git add xx/*
$ git commit -m “XXX”
#切换回 master 分支
$ git checkout master
Switched to branch ‘master’
#假设提交,且无冲突,合并分支
$ git merge dev
#使用带 -d 选项的 git branch 命令来删除分支
$ git branch -d dev
2.7 IntelliJ IDEA中使用git
目标

在idea使用git完成版本控制

git操作本地仓库(自己进行版本控制)

git操作远程仓库(多人进行版本控制)

学习路径

配置Git
创建工程集成Git
推送代码到远程仓库
从远程仓库克隆
从远程仓库拉取
使用分支
2.7.1 在Idea中配置git
安装好IntelliJ IDEA后,如果Git安装在默认路径下,那么idea会自动找到git的位置,如果更改了Git的安装位置则需要手动配置下Git的路径。

选择File→Settings打开设置窗口,找到Version Control下的git选项
选择git的安装目录后可以点击“Test”按钮测试是否正确配置。
使用idea操作github远程服务器的时候,在idea中配置用户名和密码,这样使用https协议访问github的时候,不需要输入用户名和密码了。
2.7.2 创建工程集成GIT
2.7.2.1创建maven工程
创建Maven工程打包方式为jar包,创建User类

创建本地仓库

在菜单中选择“vcs”→Import into Version Control→Create Git Repository

此时样式改变,查看本地工程目录,有绿色箭头。说明创建了本地仓库。

2.7.2.2添加暂存区
项目右键——>Git—>Add

2.7.2.3 提交本地仓库
项目右键——>Git—>Commit Directory

2.7.3 远程仓库操作
2.7.3.1 推送到远程仓库
在github上创建一个远程仓库 HelloGithub 然后将本地仓库推送到远程。

注意:这里使用https的协议连接

在工程上点击右键,选择git→Repository→Remotes,添加URL

在工程上点击右键,选择git→Repository→Push

2.7.3.2 从远程仓库克隆
方式一

选择VCS—>Checkout from Version Control—>GitHub

方式二

在idea的欢迎页上有“Checkout from version control”下拉框,选择git(推荐使用https形式的url)

2.7.3.3 修改文件push到远程仓库
修改User

先commit到本地

Git–>Commit Directory

再push到远程

Git–>Repository–>Push

2.7.3.4 从远程仓库拉取
Git–>Repository–>Pull

2.7.4 在Idea中使用分支
选择VCS—>Git—>Branches

点击“New Branch”,新建一个分支

分支操作

Checkout:为切换分支
Merge:为合并分支
Delete:删除分支
小结
在idea中配置git
将工程添加至git(操作本地仓库,将工程推送到远程仓库)
从远程仓库克隆(远程仓库)
从服务端拉取代码(远程仓库)
在idea中使用分支

一.linux基础
1.1 远程连接工具使用
在实际开发中,Linux服务器都在其他的地方,我们要通过windows客户端工具远程去连接Linux并操作 它,连接Linux的windows客户端工具有很多,企业中常用的有secureCRT、Putty、xshell、SSH Secure等。

1.2 Linux目录结构
root目录:超级管理员所在的目录,用~表示
home目录:普通用户所在的目录
usr目录:安装用户文件所在的目录
etc目录:Linux系统管理和配置文件所在的目录

1.3 文件夹(目录)操作命令
查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ls

List directory contents.

  • List files one per line:
    ls -1

  • List all files, including hidden files:
    ls -a

  • Long format list (permissions, ownership, size and modification date) of all files:
    ls -la

  • Long format list with size displayed using human readable units (KB, MB, GB):
    ls -lh

  • Long format list sorted by size (descending):
    ls -lS

  • Long format list of all files, sorted by modification date (oldest first):
    ls -ltr
    跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cd

Change the current working directory.

  • Go to the given directory:
    cd path/to/directory

  • Go to home directory of current user:
    cd

  • Go up to the parent of the current directory:
    cd ..

  • Go to the previously chosen directory:
    cd -
    创建

1
2
3
4
5
6
7
8
9
mkdir

Creates a directory.

  • Create a directory in current directory or given path:
    mkdir directory

  • Create directories recursively (useful for creating nested dirs):
    mkdir -p path/to/directory
    搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
find

Find files or directories under the given directory tree, recursively.

  • Find files by extension:
    find root_path -name ‘*.ext’

  • Find files by matching multiple patterns:
    find root_path -name ‘pattern_1‘ -or -name ‘pattern_2

  • Find directories matching a given name, in case-insensitive mode:
    find root_path -type d -iname ‘lib

  • Find files matching a path pattern:
    find root_path -path ‘/lib//*.ext’

  • Find files matching a given pattern, excluding specific paths:
    find root_path -name ‘.py’ -not -path ‘/site-packages/*’

  • Find files matching a given size range:
    find root_path -size +500k -size -10M

  • Run a command for each file (use {} within the command to access the filename):
    find root_path -name ‘*.ext’ -exec wc -l {} ;

  • Find files modified in the last 7 days, and delete them:
    find root_path -mtime -7 -delete
    修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

mv

Move or rename files and directories.

  • Move files in arbitrary locations:
    mv source target

  • Do not prompt for confirmation before overwriting existing files:
    mv -f source target

  • Prompt for confirmation before overwriting existing files, regardless of file permissions:
    mv -i source target

  • Do not overwrite existing files at the target:
    mv -n source target

  • Move files in verbose mode, showing files after they are moved:
    mv -v source target
    剪切

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

mv

Move or rename files and directories.

  • Move files in arbitrary locations:
    mv source target

  • Do not prompt for confirmation before overwriting existing files:
    mv -f source target

  • Prompt for confirmation before overwriting existing files, regardless of file permissions:
    mv -i source target

  • Do not overwrite existing files at the target:
    mv -n source target

  • Move files in verbose mode, showing files after they are moved:
    mv -v source target
    复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cp

Copy files and directories.

  • Copy a file to another location:
    cp path/to/file.ext path/to/copy.ext

  • Copy a file into another directory, keeping the filename:
    cp path/to/file.ext path/to/target_parent_directory

  • Recursively copy a directory’s contents to another location (if the destination exists, the directory is copied inside it):
    cp -r path/to/directory path/to/copy

  • Copy a directory recursively, in verbose mode (shows files as they are copied):
    cp -vr path/to/directory path/to/copy

  • Copy text files to another location, in interactive mode (prompts user before overwriting):
    cp -i *.txt path/to/target_directory

  • Dereference symbolic links before copying:
    cp -L link path/to/copy
    删除

1
2
3
4
5
6
7
8
9
rmdir

Removes a directory.

  • Remove directory, provided it is empty. Use rm to remove not empty directories:
    rmdir path/to/directory

  • Remove directories recursively (useful for nested dirs):
    rmdir -p path/to/directory
    1.4 文件操作命令
    创建

1
2
3
4
5
6
7
8
9
10
11
12
touch

Change a file access and modification times (atime, mtime).

  • Create a new empty file(s) or change the times for existing file(s) to current time:
    touch filename

  • Set the times on a file to a specific date and time:
    touch -t YYYYMMDDHHMM.SS filename

  • Use the times from a file to set the times on a second file:
    touch -r filename filename2
    查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
cat

Print and concatenate files.

  • Print the contents of a file to the standard output:
    cat file

  • Concatenate several files into the target file:
    cat file1 file2 > target_file

  • Append several files into the target file:
    cat file1 file2 >> target_file

  • Number all output lines:
    cat -n file

  • Display non-printable and whitespace characters (with M- prefix if non-ASCII):
    cat -v -t -e file

tail

Display the last part of a file.

  • Show last ‘num’ lines in file:
    tail -n num file

  • Show all file since line ‘num’:
    tail -n +num file

  • Show last ‘num’ bytes in file:
    tail -c num file

  • Keep reading file until Ctrl + C:
    tail -f file

  • Keep reading file until Ctrl + C, even if the file is rotated:
    tail -F file

head

Output the first part of files.

  • Output the first few lines of a file:
    head -n count_of_lines filename

  • Output the first few bytes of a file:
    head -c number_in_bytes filename
    删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
rm

Remove files or directories.

  • Remove files from arbitrary locations:
    rm path/to/file path/to/another/file

  • Recursively remove a directory and all its subdirectories:
    rm -r path/to/directory

  • Forcibly remove a directory, without prompting for confirmation or showing error messages:
    rm -rf path/to/directory

  • Interactively remove multiple files, with a prompt before every removal:
    rm -i file(s)

  • Remove files in verbose mode, printing a message for each removed file:
    rm -v path/to/directory/*
    编辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
vim

Vim (Vi IMproved), a command-line text editor, provides several modes for different kinds of text manipulation.
Pressing i enters edit mode. <Esc> goes back to normal mode, which doesn’t allow regular text insertion.
More information: https://www.vim.org.

  • Open a file:
    vim path/to/file

  • View Vim’s help manual:
    :help

  • Save a file:
    :write

  • Quit without saving:
    :quit!

  • Open a file at a specified line number:
    vim +line_number path/to/file

  • Undo the last operation:
    u

  • Search for a pattern in the file (press n/N to go to next/previous match):
    /search_pattern

  • Perform a regex substitution in the whole file:
    :%s/pattern/replacement/g
    1.5 文件压缩命令
    将文件打成压缩包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
tar

Archiving utility.
Often combined with a compression method, such as gzip or bzip.
More information: https://www.gnu.org/software/tar.

  • Create an archive from files:
    tar cf target.tar file1 file2 file3

  • Create a gzipped archive:
    tar czf target.tar.gz file1 file2 file3

  • Create a gzipped archive from a directory using relative paths:
    tar czf target.tar.gz -C path/to/directory .

  • Extract a (compressed) archive into the current directory:
    tar xf source.tar[.gz|.bz2|.xz]

  • Extract an archive into a target directory:
    tar xf source.tar -C directory

  • Create a compressed archive, using archive suffix to determine the compression program:
    tar caf target.tar.xz file1 file2 file3

  • List the contents of a tar file:
    tar tvf source.tar

  • Extract files matching a pattern:
    tar xf source.tar –wildcards “*.html”

  • Extract a specific file without preserving the folder structure:
    tar xf source.tar source.tar/path/to/extract –strip-components=depth_to_strip
    解压缩包获得文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
tar

Archiving utility.
Often combined with a compression method, such as gzip or bzip.
More information: https://www.gnu.org/software/tar.

  • Create an archive from files:
    tar cf target.tar file1 file2 file3

  • Create a gzipped archive:
    tar czf target.tar.gz file1 file2 file3

  • Create a gzipped archive from a directory using relative paths:
    tar czf target.tar.gz -C path/to/directory .

  • Extract a (compressed) archive into the current directory:
    tar xf source.tar[.gz|.bz2|.xz]

  • Extract an archive into a target directory:
    tar xf source.tar -C directory

  • Create a compressed archive, using archive suffix to determine the compression program:
    tar caf target.tar.xz file1 file2 file3

  • List the contents of a tar file:
    tar tvf source.tar

  • Extract files matching a pattern:
    tar xf source.tar –wildcards “*.html”

  • Extract a specific file without preserving the folder structure:
    tar xf source.tar source.tar/path/to/extract –strip-components=depth_to_strip
    1.6 文件权限命令
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    chmod

Change the access permissions of a file or directory.

  • Give the [u]ser who owns a file the right to e[x]ecute it:
    chmod u+x file

  • Give the [u]ser rights to [r]ead and [w]rite to a file/directory:
    chmod u+rw file_or_directory

  • Remove e[x]ecutable rights from the [g]roup:
    chmod g-x file

  • Give [a]ll users rights to [r]ead and e[x]ecute:
    chmod a+rx file

  • Give [o]thers (not in the file owner’s group) the same rights as the [g]roup:
    chmod o=g file

  • Remove all rights from [o]thers:
    chmod o= file

  • Change permissions recursively giving [g]roup and [o]thers the abililty to [w]rite:
    chmod -R g+w,o+w directory
    1.7 其他命令
    显示工作目录

1
2
3
4
5
6
7
8
9
pwd

Print name of current/working directory.

  • Print the current directory:
    pwd

  • Print the current directory, and resolve all symlinks (i.e. show the “physical” path):
    pwd -P
    查看进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ps

Information about running processes.

  • List all running processes:
    ps aux

  • List all running processes including the full command string:
    ps auxww

  • Search for a process that matches a string:
    ps aux | grep string

  • List all processes of the current user in extra full format:
    ps –user $(id -u) -F

  • List all processes of the current user as a tree:
    ps –user $(id -u) f

  • Get the parent pid of a process:
    ps -o ppid= -p pid
    杀死进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
kill

Sends a signal to a process, usually related to stopping the process.
All signals except for SIGKILL and SIGSTOP can be intercepted by the process to perform a clean exit.

  • Terminate a program using the default SIGTERM (terminate) signal:
    kill process_id

  • List available signal names (to be used without the SIG prefix):
    kill -l

  • Terminate a background job:
    kill %job_id

  • Terminate a program using the SIGHUP (hang up) signal. Many daemons will reload instead of terminating:
    kill -1|HUP process_id

  • Terminate a program using the SIGINT (interrupt) signal. This is typically initiated by the user pressing Ctrl + C:
    kill -2|INT process_id

  • Signal the operating system to immediately terminate a program (which gets no chance to capture the signal):
    kill -9|KILL process_id

  • Signal the operating system to pause a program until a SIGCONT (“continue”) signal is received:
    kill -17|STOP process_id

  • Send a SIGUSR1 signal to all processes with the given GID (group id):
    kill -SIGUSR1 -group_id
    搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
grep

Matches patterns in input text.
Supports simple patterns and regular expressions.

  • Search for an exact string:
    grep search_string path/to/file

  • Search in case-insensitive mode:
    grep -i search_string path/to/file

  • Search recursively (ignoring non-text files) in current directory for an exact string:
    grep -RI search_string .

  • Use extended regular expressions (supporting ?, +, {}, () and |):
    grep -E ^regex$ path/to/file

  • Print 3 lines of [C]ontext around, [B]efore, or [A]fter each match:
    grep -C|B|A 3 search_string path/to/file

  • Print file name with the corresponding line number for each match:
    grep -Hn search_string path/to/file

  • Use the standard input instead of a file:
    cat path/to/file | grep search_string

  • Invert match for excluding specific strings:
    grep -v search_string
    管道

1
2
3
4
| ##管道符
管道只允许正确输出通过
tee ####复制一份输出
2>&1 | ####转换错误输出为正确再通过管道
关机

1
poweroff
重启

1
2
3
4
5
6
7
8
9
reboot

Reboot the system.

  • Reboot immediately:
    sudo reboot

  • Reboot immediately without gracefully shutting down:
    sudo reboot -q
    二.Linux网络
    2.1 网络服务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ifconfig

Network Interface Configurator.

  • View network settings of an ethernet adapter:
    ifconfig eth0

  • Display details of all interfaces, including disabled interfaces:
    ifconfig -a

  • Disable eth0 interface:
    ifconfig eth0 down

  • Enable eth0 interface:
    ifconfig eth0 up

  • Assign IP address to eth0 interface:
    ifconfig eth0 ip_address

ip addr :CentOS7版本
修改网卡ip
vim /etc/sysconfig/network-scripts/ifcfg-ens33
概要信息
DEVICE=ens33 网卡名称
TYPE=Ethernet 网卡类型 以太网
ONBOOT=yes 是否开机就使用此网卡
BOOTPROTO=dhcp 启动网卡时指定获取IP地址的方式
IPADDR= ip地址
GATEWAY= 网关
NETMASK=255.255.255.0 子网掩码
DNS1= DNS服务器
重启网卡服务
systemctl status network 查看指定服务的状态
systemctl stop network 停止指定服务
systemctl start network 启动指定服务
systemctl restart network 重启指定服务
2.2 防火墙服务
2.2.1 防火墙设置
防火墙设置
开启防火墙 systemctl start firewalld
重启防火墙 systemctl restart firewalld
关闭防火墙 systemctl stop firewalld
设置开机启动 systemctl enable firewalld
停止并关闭开机启动 systemctl disable firewalld
查看防火墙状态 systemctl status firewalld 或者 firewall-cmd –state
查看防火墙开机时是否启动 systemctl list-unit-files | grep firewalld
2.2.2 端口设置
端口设置
添加 firewall-cmd –zone=public –add-port=80/tcp –permanent
更新防火墙规则 firewall-cmd –reload
查看 firewall-cmd –zone=public –query-port=80/tcp
firewall-cmd –zone=public –list-ports
删除 firewall-cmd –zone=public –remove-port=80/tcp –permanent
常用端口 8080 tomcat
80 http协议
443 https协议
22 ssh远程连接
3306 mysql
6379 redis
三.Nginx
3.1 介绍
Nginx是一款轻量级的 Web 服务器,由俄罗斯的程序设计师伊戈尔·西索夫所开发。 Nginx性能非常优秀, 官方测试能够支撑5万并发链接,并且 cpu、内存等资源消耗却非常低,运行非常稳定。

Nginx的功能有很多,我们主要使用它来做静态资源服务器、负载均衡服务器和反向代理服务器。

3.2 应用场景
3.2.1 静态资源服务器
部署网站的静态资源(html、css、js),可与Tomcat等实现动静分离

3.2.2 反向代理服务器
代理

给某个对象提供一个代理对象,并由代理对象控制原对象的引用

正向代理

对客户端进行代理(例如VPN)

反向代理

对服务端进行代理

反向代理,就是对服务端进行代理,作为客户端,只需要将请求发送到反向代理服务器,由反向代理服务 器去选择目标服务器获取数据后,再响应给客户端,此时反向代理服务器和目标服务器对外就是一个服 务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。

3.2.3 负载均衡服务器
负载均衡 Load Balance 意思就是将一份负载分摊到多个操作单元上进行执行

3.3 配置文件介绍(nginx-1.13.9/conf/nginx.conf)
1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80; #port
server_name localhost; #Server

#charset koi8-r;

#access_log  logs/host.access.log  main;

location / {
    root   html; #Static resource directory
    index  index.html index.htm; #Default visit page
}

3.5 nginx与tomcat区别
存放的文件(资源)形式
Nginx是http服务器,只能解析静态文件

Tomcat是web中间件(本质上是一个servlet),能解析jsp和静态文件

用途

nginx可以作为反向代理服务器,负责均衡服务器,静态资源存放服务器

tomcat能作为jsp容器使用,静态资源存放服务器

性能

nginx支持5W+并发,tomcat的并发只能在200-400之间

总结

linux基础

Linux基本命令

目录结构

  • root

​ - 超级管理员所在的目录

  • home

​ - 普通用户所在的目录

  • usr

​ - 安装用户文件所在的目录

  • etc

​ - Linux系统管理和配置文件所在的目录

目录操作

  • 查看

​ - ll

​ - ls

  • 切换

​ - cd

​ - 绝对路径

​ - 相对路径

  • 创建

​ - mkdir -p

  • 修改/剪切

​ - mv

  • 复制

​ - cp -r

  • 删除

​ - rmdir -p

文件操作

  • 创建

​ - touch

  • 查看

​ - cat/more/less/head/tail

  • 删除

​ - rm -rf

  • 编辑

​ - vim

​ - 插入模式

​ - i

​ - 命令行模式

​ - esc

​ - 底行模式

​ - :

​ - wq!

​ - q!

压缩命令

  • 打包

​ - tar -zcvf

​ - xxxx.tar.gz

  • 解压缩

​ - tar -zxvf

​ - xxxx.tar.gz

文件权限

  • chmod -R 777

其他命令

  • 查看进程

​ - ps -ef

  • 杀死进程

​ - kill -9 PID

  • 文本搜索

​ - grep -in

  • 管道

​ - 一个命令的输出作为另一个命令的输入

Linux网络

网络

  • 查看ip

​ - ifconfig

​ - ip addr

防火墙

  • 防火墙设置

​ - 关闭

​ - systemctl stop firewalld

​ - 关闭开机自启动

​ - systemctl disable firewalld

  • 端口设置

​ - 放行端口

​ - firewall-cmd –zone=public –add-port=80/tcp –permanent

​ - 更新规则

​ - firewall-cmd –reload

Nginx

由战斗民族开发的一款高性能的 http 服务器/反向代理服务器

  • http服务器

  • 反向代理(负载均衡)