一、 什么是服务注册中心
服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来 存储服务信息,譬如提供者 url 串、路由信息等。服务注册中心是 SOA 架构中最基础的设 施之一。
1 服务注册中心的作用
1,服务的注册 2,服务的发现
举例:QQ群里面的群成员就是一个服务的注册;群成员发消息就是服务的发现.
2 常见的注册中心有哪些
1,Dubbo 的注册中心 Zookeeper
2,Sringcloud 的注册中心 Eureka
3 服务注册中心解决了什么问题
1.服务管理
2.服务的依赖关系管理
4 什么是 Eureka 注册中心
Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 spring-cloud-netflix 中,以实现 Spring Cloud 的服务注册于发现,同时 还提供了负载均衡、故障转移等能力。
5 Eureka 注册中心三种角色
5.1EurekaServer
通过 Register、Get、Renew 等接口提供服务的注册和发现。
5.2ApplicationService(ServiceProvider)
服务提供方 把自身的服务实例注册到 Eureka Server 中
5.3ApplicationClient(ServiceConsumer)
服务调用方 通过 EurekaServer 获取服务列表,消费服务。
二、 Eureka 入门案例
1 创建项目
2 修改 pom 文件添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt</groupId>
<artifactId>springcloud-eureka-server-ha</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-server-ha</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3 修改启动类
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
4 修改 application.properties 全局配置文件
spring.application.name=eureka-server
server.port=8761
#是否将自己注册到 Eureka-Server 中,默认的为 true
eureka.client.registerWithEureka=false
#是否冲 Eureka-Server 中获取服务注册信息,默认为 true
eureka.client.fetchRegistry=false
5 通过浏览器访问 Eureka-Server 服务管理平台
三、 搭建高可用 Eureka 注册中心(Eureka 集群)
1 创建项目
2 配置文件
#设置 eureka 实例名称,与配置文件的变量为主
eureka.instance.hostname=eureka2
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/
在搭建 Eureka 集群时,需要添加多个配置文件,并且使用 SpringBoot 的多环境配置方 式。集群中需要多少节点就添加多少个配置文件。
3 在配置文件中配置集群节点
3.1eureka1
#设置 eureka 实例名称,与配置文件的变量为主
eureka.instance.hostname=eureka1
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://eureka2:8761/eureka/
3.2eureka2
#设置 eureka 实例名称,与配置文件的变量为主
eureka.instance.hostname=eureka2
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/
4 添加 logback 日志配置文件
5 Eureka 集群部署
部署环境:需要安装 jdk1.8,正确配置环境变量。 注意:需要关闭 linux 的防火墙,或者是开放 8761 端口
5.1将项目打包
Maveninstall
5.2上传实例
在/usr/local/创建一个 eureka 的目录 将项目的 jar 包拷贝到/usr/local/eureka
6 编写一个启动脚本文件
cd `dirname $0`
CUR_SHELL_DIR=`pwd`
CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
JAR_NAME="项目名称"
JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME
#JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m"
JAVA_MEM_OPTS=""
SPRING_PROFILES_ACTIV="eureka1"
#SPRING_PROFILES_ACTIV="-Dspring.profiles.active=配置文件变量名称"
LOG_DIR=$CUR_SHELL_DIR/logs
LOG_PATH=$LOG_DIR/${JAR_NAME%..log
echo_help()
{
echo -e "syntax: sh $CUR_SHELL_NAME start|stop"
}
if [ -z $1 ];then
echo_help
exit 1
fi
if [ ! -d "$LOG_DIR" ];then
mkdir "$LOG_DIR"
fi
if [ ! -f "$LOG_PATH" ];then
touch "$LOG_DIR"
fi
if [ "$1" == "start" ];then
# check server
PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
if [ -n "$PIDS" ]; then
echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}."
exit 1
fi
echo "Starting the $JAR_NAME..."
# start
nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 &
COUNT=0
while [ $COUNT -lt 1 ]; do
sleep 1
COUNT=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`
if [ $COUNT -gt 0 ]; then
break
fi
done
PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'`
echo "${JAR_NAME} Started and the PID is ${PIDS}."
echo "You can check the log file in ${LOG_PATH} for details."
elif [ "$1" == "stop" ];then
PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
if [ -z "$PIDS" ]; then
echo "ERROR:The $JAR_NAME does not started!"
exit 1
fi
echo -e "Stopping the $JAR_NAME..."
for PID in $PIDS; do
kill $PID > /dev/null 2>&1
done
COUNT=0
while [ $COUNT -lt 1 ]; do
sleep 1
COUNT=1
for PID in $PIDS ; do
PID_EXIST=`ps --no-heading -p $PID`
if [ -n "$PID_EXIST" ]; then
COUNT=0
break
fi
done
done
echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}."
else
echo_help
exit 1
fi
6.1设置启动脚本的运行权限
Chmod-R755server.sh
7 修改 linux 的 host 文件
Vim /etc/hosts
192.168.70.134 eureka1
192.168.70.135 eureka2
8 启动 eureka 注册中心
./server.shstart 启动
./server.shstop 停止
9 通过浏览器访问注册中心的管理页面
四、 在高可用的 Eureka 注册中心中构建 provider 服务
1 创建项目
2 修改 pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt</groupId>
<artifactId>springcloud-eureka-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-provider</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3 修改启动类
@EnableEurekaClient
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
4 修改 provider 的配置文件
spring.application.name=eureka-provider
server.port=9090
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8761/eureka/
5 修改 windows 的 host 文件
路径:C:\Windows\System32\drivers\etc
192.168.70.134 eureka1
192.168.70.135 eureka2
6 编写服务接口
6.1创建接口
@RestController
public class UserController {
@RequestMapping("/user")
public List<User> getUsers() {
List<User> list = new ArrayList<>();
list.add(new User(1, "zhangsan", 20));
list.add(new User(2, "lisi", 22));
list.add(new User(3, "wangwu", 20));
return list;
}
}
6.2创建 pojo
public class User {
private int userid;
private String username;
private int userage;
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
public User(int userid, String username, int userage) {
super();
this.userid = userid;
this.username = username;
this.userage = userage;
}
public User() {
super();
// TODO Auto-generated constructor stub
}
}
五、 在高可用的 Eureka 注册中心中构建 consumer 服务
1 创建项目
服务的消费者与提供者都需要再 Eureka 注册中心注册。
2 Consumer 的配置文件
spring.application.name=eureka-provider
server.port=9091
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8761/eureka/
3 在 Service 中完成服务的调用
@Service
public class UserService {
@Autowired
private LoadBalancerClient loadBalancerClient;//ribbon负载均衡器
public List<User> getUsers() {
//选择调用的服务的名称
//ServiceInstance 封装了服务的基本信息,如 IP,端口
ServiceInstance si = this.loadBalancerClient.choose("eureka-provider");
//拼接访问服务的URL
StringBuffer sb = new StringBuffer();
//http://localhost:9090/user
sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user");
//springMVC RestTemplate
RestTemplate rt = new RestTemplate();
ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {
};
//ResponseEntity:封装了返回值信息
ResponseEntity<List<User>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
List<User> list = response.getBody();
return list;
}
}
4 Controller
@RestController public class UserController {
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/consumer")
public List<User> getUsers() {
return this.userService.getUsers();
}
}
六、 Eureka 注册中心架构原理
1 Eureka 架构图
Register(服务注册):把自己的 IP 和端口注册给 Eureka。 Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。 Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防 止 consumer 调用到不存在的服务。 GetRegistry(获取服务注册列表):获取其他服务列表。 Replicate(集群中数据同步):eureka 集群中的数据复制与同步。 MakeRemoteCall(远程调用):完成服务的远程调用。
七、 基于分布式 CAP 定理,分析注册中心两大主流框架:Eureka 与 Zookeeper 的区别
1 什么是 CAP 原则
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。 CAP 由 Eric Brewer 在 2000 年 PODC 会议上提出。该猜想在提出两年后被证明成 立,成为我们熟知的 CAP 定理
2 Zookeeper 与 Eureka 的区别
八、 Eureka 优雅停服
1 在什么条件下,Eureka 会启动自我保护?
2 为什么要启动自我保护
3 如何关闭自我保护
修改 EurekaServer 配置文件
#关闭自我保护:true 为开启自我保护,false 为关闭自我保护
eureka.server.enableSelfPreservation=false
#清理间隔(单位:毫秒,默认是 60*1000)
eureka.server.eviction.interval-timer-in-ms=60000
4 如何优雅停服
4.1不需要再 EurekaServer 中配置关闭自我保护
4.2需要再服务中添加 actuator.jar 包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt</groupId>
<artifactId>springcloud-eureka-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-eureka-provider</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.3修改配置文件
#启用 shutdown endpoints.shutdown.
enabled=true
#禁用密码验证
endpoints.shutdown.sensitive=false
4.4发送一个关闭服务的 URL 请求
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static void main(String[] args) {
String url ="http://127.0.0.1:9090/shutdown";
//该url必须要使用dopost方式来发送
HttpClientUtil.doPost(url);
}
}
九、 如何加强 Eureka 注册中心的安全认证
1 在 EurekaServer 中添加 security 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2 修改 EurekaServer 配置文件
#开启 http basic 的安全认证
security.basic.enabled=true
security.user.name=user
security.user.password=123456
3 修改访问集群节点的 url
eureka.client.serviceUrl.defaultZone=http://user:123456@eur eka2:8761/eureka/
4 修改微服务的配置文件添加访问注册中心的用户名与密码
spring.application.name=eureka-provider
server.port=9090
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
#启用shutdown
endpoints.shutdown.enabled=true
#禁用密码验证
endpoints.shutdown.sensitive=false