技术需求点:
使用SpringBoot和SpringCloud Eureka搭建一个高可用的服务注册、管理与调用中心,搭建服务调用者和消费者演示如何注册、管理和调用服务,顺带介绍下Spring Eureka的原理。
其中:
1.Eureka Server搭建使用集群结构,2个节点分布于2台机器上;
一.SpringCloud Eureka集群架构
使用Eureka搭建服务注册中心需要区分服务端和客户端,服务端用来注册和管理业务服务,任何业务服务都需要通过服务端注册之后才能提供服务或消费服务,因此,Eureka服务端提供了一种注册和管理业务服务的服务(听起来有点绕)。客户端分为两个角色,业务服务提供者和业务服务消费者,作用根据其字面意思即可理解。因此,这个架构中有三个角色:Eureka Server(注册与管理服务服务端)、Application Servicer(业务服务提供者)、Application Consumer(业务服务消费者)。
举个例子,如下图所示:Application Servicer提供了一个用户查询信息的服务,Application Consumer要远程调用Application Service的查询服务去查询用户信息,在使用之前,这两个服务都必须在Eureka Server中注册登记才行,否则谁知道有哪些服务可用呢?注册登记之后,Eureka Server就可以对所有服务(无论是提供者还是消费者)进行管理,包括服务注册、续约、下线、提供服务列表等,而且为了保证挂了一个节点之后,Eureka Server的高可用,需要把Eureka Server做成集群形式。为了达到这样的目的,本次搭建就可以做成下面架构图的样子了。
解释一下:
Register(服务注册):把自己的 IP 和端口注册给 Eureka。
Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防 止 consumer 调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Replicate(集群中数据同步):Eureka 集群中的数据复制与同步。
Make Remote Call(远程调用):完成服务的远程调用。
- 准备工作
本次构建的节点规划包括:两个Eureka Server集群节点,一个业务提供者Application Servicer和一个业务消费者Application Consumer,注意起的项目名字,下面使用项目名称来介绍各个角色,整体节点规划如下表所示:
主机名 | Ip:Port | 角色 |
---|---|---|
springcloud-eureka-server1 | 192.168.33.100:8761 | Eureka Server(注册服务服务端) |
springcloud-eureka-server2 | 192.168.33.101:8761 | Eureka Server(注册服务服务端) |
springcloud-eureka-provider | Localhost(127.0.0.1):9090 | Application Servicer(业务服务提供者) |
springcloud-eureka-consumer | Localhost(127.0.0.1):9091 | Application consumer(业务服务消费者) |
二.搭建高可用Eureka注册中心(Eureka Server集群)
1.项目结构
2.pom文件
4.0.0
com.third
springcloud_eureka
2.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
1.5.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.cloud
spring-cloud-dependencies
Dalston.SR5
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-starter-config
org.springframework.cloud
spring-cloud-starter-eureka-server
ch.qos.logback
logback-classic
1.2.3
org.springframework.boot
spring-boot-maven-plugin
3.启动类EurekaApplication
Server端使用@EnableEurekaServer注解
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
4.在配置文件中配置集群节点
这里要说明下,server端最后打的jar包是通用的,分别传到两台机器上之后,使用不同的配置文件启动,所以这里有两个配置文件。在搭建Eureka集群时,添加多个配置文件,并使用SpringBoot的多环境配置方式,集群中需要多少节点就添加多少配置文件。
另外,需要在linux两台机器上配置host:
vi /etc/hosts
192.168.33.100 eureka1
192.168.33.101 eureka2
4.1 application-eureka1.properties
spring.application.name=eureka-server
server.port=8761
#eureka实例名称
eureka.instance.hostname=eureka1
#设置服务注册中心地址,指向另一个注册中心,用于数据传输和通信
eureka.client.serviceUrl.defaultZone=http://eureka2:8761/eureka/
4.2 application-eureka2.properties
spring.application.name=eureka-server
server.port=8761
#eureka实例名称
eureka.instance.hostname=eureka2
#设置服务注册中心地址,指向另一个注册中心,用于数据传输和通信
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/
5.Eureka集群部署
部署环境:jdk1.8,正确配置环境变量,且关闭linux的防火墙(systemctl stop firewalld.service或service iptables stop)
maven打jar包,之后上传到【192.168.33.100、192.168.33.101】两台机器上,目录可以自己指定,我的目录是/data/program/eureka。
6.启动脚本
注意:windows编辑的文本到linux上可能换行符之类的符号发生变化,自行百度格式化下。
启动脚本要修稿2个地方:
- JAR_NAME="springcloud_eureka-2.0-SNAPSHOT.jar",值(springcloud_eureka-2.0-SNAPSHOT.jar)要填写你自己maven打的jar包名;
- SPRING_PROFILES_ACTIV="-Dspring.profiles.active=eureka1",值(eureka1)要填写你的变量文件的变量部分,我的配置文件名字(application-eureka1.properties)
#!/bin/bash
cd `dirname $0`
CUR_SHELL_DIR=`pwd`
CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
JAR_NAME="springcloud_eureka-2.0-SNAPSHOT.jar"
JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME
#JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m"
JAVA_MEM_OPTS=""
SPRING_PROFILES_ACTIV="-Dspring.profiles.active=eureka1"
#SPRING_PROFILES_ACTIV=""
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
7.启动eureka注册中心
./server.shstart 启动
./server.shstop 停止
项目中用logback添加了日志,配置文件logback.xml,打印在/data/program/eureka/catalina.base_IS_UNDEFINED/logs目录下。
通过浏览器访问注册中心的管理页面【192.168.33.100:8761、192.168.33.101:8761】,如果看到以下界面,证明Eureka Server集群搭建成功(Instance currently...处,此时应该只有第三个Eureka Server的服务注册上来,我这个截图是截的最后的)。
三.在高可用的Eureka注册中心构建provider服务
1.项目结构
2.pom文件
与server的pom基本一致,不一样的地方是client的artifactId用的是spring-cloud-starter-eureka
4.0.0
com.third
springcloud-eureka-provider
3.0-SNAPSHOT
jar
org.springframework.boot
spring-boot-starter-parent
1.5.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.cloud
spring-cloud-dependencies
Dalston.SR5
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-starter-config
org.springframework.cloud
spring-cloud-starter-eureka
ch.qos.logback
logback-classic
1.2.3
org.springframework.boot
spring-boot-maven-plugin
3.启动类
client端用的是@EnableEurekaClient注解
@EnableEurekaClient
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
4.provider的配置文件
该服务名字是eureka-provider,该服务提供者把自己的服务注册给Eureka Server注册中心,因为是集群,所以注册给2个节点
spring.application.name=eureka-provider
server.port=9090
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://192.168.33.100:8761/eureka/,http://192.168.33.101:8761/eureka/
5.编写服务接口
5.1 创建接口
@RestController
public class UserController {
@RequestMapping("/user")
public List getUsers(){
List list=new ArrayList<>();
list.add(new User(1,"zhagnsan",20));
list.add(new User(2,"lisi",21));
list.add(new User(3,"wangwu",22));
return list;
}
}
5.2 创建实体
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) {
this.userid = userid;
this.username = username;
this.userage = userage;
}
public User() {
super();
}
}
启动工程后,可以看到eureka-provider服务提供者已经注册到注册中心,调用UserConller的接口也可以调通。
四.在高可用的Eureka注册中心构建consumer服务
1.项目结构
2.pom文件与provider的服务除了项目名不同其它完全一致
3.Consumer的配置文件
同样,消费者也需要注册到注册中心
spring.application.name=eureka-consumer
server.port=9091
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://192.168.33.100:8761/eureka/,http://192.168.33.101:8761/eureka/
4.创建service,在service中完成服务的调用
在consumer中写了个service,在service中通过http请求的形式调用了provider提供的服务。
注意:ServiceInstance .getHost()方法获取host时,有时会获取到你计算机的名字,而不是localhost,我没有深究,拼接的host那里直接写的127.0.0.1,感兴趣的朋友可以研究下。
@Service
public class UserService {
@Autowired
private LoadBalancerClient loadBalancerClient;//ribbon负载均衡
public List getUsers(){
//选择调用的服务的名称,ServiceInstance 封装了服务的基本信息,如IP,端口等
ServiceInstance si=this.loadBalancerClient.choose("eureka-provider");
//拼接访问服务的URL:http://localhost:9090/user
StringBuffer sb = new StringBuffer();
sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user");
//springMVC restTemplate
RestTemplate rt=new RestTemplate();
ParameterizedTypeReference> type =new ParameterizedTypeReference>() {};
//ResponseEntity:封装了返回值信息
ResponseEntity> response=rt.exchange(sb.toString(), HttpMethod.GET,null,type);
List list=response.getBody();
return list;
}
}
5.controller
@RestController
public class ConsumerController {
@Autowired
private UserService userService;
@RequestMapping("/consumer")
public List getUsers(){
return userService.getUsers();
}
}
User与provider中的一样,这个服务中也需要copy一份,运行起来,可以看到consumer也被注册到了Eureka
Server注册中心中,调用接口【127.0.0.1:9091/consumer】,发现返回了provider中"/user"接口的处理结果,说明整个项目搭建成功!
后记
由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!