目录
一.理论基础
二.父项目
2.1 新建父项目
2.2 管理依赖
三.子项目
3.1 新建子项目
3.2 注册中心Server依赖和启动类和配置文件
3.3 生产者Client 依赖和启动类和配置文件
3.5 消费者Custmer依赖和配置类、启动类和配置文件
四.心跳
五.公共资源项目
5.1新建实体类
5.2使用
5.3效果
六.公共依赖
七.页面和前后端数据传输(restTemplate)
7.1页面
7.2后端向页面放值
7.3页面向后端提交
一.理论基础
eureka,是Netflix开发的服务发现框架,为了达到负载均衡和故障转移的目的。集成在Spring-cloud-netflix中,实现SpringCloud服务发现功能。
其中包含两个组件:Eureka Server,Eureka Client
Eureka Server提供服务注册服务,是服务注册中心,启动后会在Eureka Server中进行注册,服务的信息可以在Eureka的显示页面中显示。
Eureka Client是生产者。
二.父项目
2.1 新建父项目
java项目,使用Maven。不需要父项目的src文件
2.2 管理依赖
管理依赖的版本号,并将版本号传递给子项目,此时只是管理,并不会下载依赖。springBoot和SpringCloud的版本号需要相互对应。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2022.0.0</version>
<!--将版本号传递给子项目,必须加-->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
SpringCloud | SpringBoot |
Finchley | 2.0.x |
Finchley.SR1 | Spring Boot >=2.0.3.RELEASE and <=2.0.9RELEASE |
Finchley.SR4 | Spring Boot >=2.0.3.RELEASE and <=2.0.9RELEASE |
Greenwich | 2.1.x |
Hoxton | 2.2.x,2.3.x (Starting with SR5) |
2020.0.x aka Ilford | 2.4.x |
三.子项目
3.1 新建子项目
方法一:
在父项目中右键选择Module,将会自动添加父项目pom文件中的modules
方法二:
将已经完成的eurekaClient复制,更改子项目pom文件中的artifactId,手动添加父项目的pom文件中的modul。
3.2 注册中心Server依赖和启动类和配置文件
加入web依赖和server依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
启动类中,加上开启注册中心的注解@EnableEurekaServer,并且按SpringBoot的方式写启动类。
//开启注册中心
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
新建配置文件,注册服务中心的端口号以7开头,进行名称和地址配置,Eureka Server不能向Eureka Server中注册,所以设置为false。
# 服务器端口号7000
server:
port: 7000
spring:
application:
#端口号名称配置
name: eureka-server
eureka:
client:
# 表示是否向Eureka Server注册
fetch-registry: false
# 表示是否从Eureka Server获取注册信息
register-with-eureka: false
#设置服务注册中心地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#设置访问地址
instance:
hostname: localhost
启动EurekaServer----http://localhost:7000,即可访问Eureka。
1.SystemStatus
Environment:环境,默认值为test,在实际使用过程中,可以不用更改
Data center:数据中心,在Hoxton.SR12版本中默认值为default,网上其他博客大部分都解释默认值为MyOwn,预测是版本的不同该值也不同
Current time:当前服务器时间
Uptime:已经运行时长,单位:时:分
Lease expiration enabled:租约过期是否启用,该值与自我保护机制相关,自我保护关闭时该值为true,开启时为false
Renews threshold:每分钟最少续约数,算法公式:2 * n * 0.85,n表示节点数量(包括注册中心)
Renews (last min):最后一分钟的续约数量(接受到的心跳数,不含当前,每分钟更新一次),当该值除以Renews threshold的结果小于等于1时会触发自我保护机制的开启
2.DS Replicas----Eureka 集群节点副本
Instances currently registered with Eureka;当前注册到Eureka的实例
Application:实例名称
Availability Zones:可用区域数量,也就是该实例节点数
Status:实例状态(包括实例ID)
3.General Info----一般信息
total-avail-memory:总共可用内存
environment:环境名称
num-of-cpus:CPU数量(包括逻辑处理器)
current-memory-usage:当前已使用内存百分比
server-uptime:服务已经运行时长,单位:时:分
registered-replicas:向其他注册中心注册的副本地址
unavailable-replicas:不可用的副本地址
available-replicas:可用的副本地址
4.Instance Info ----实例信息
ipAddr:当前实例IP地址
status:当前实例状态
3.3 生产者Client 依赖和启动类和配置文件
加入web依赖和client依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
启动类中,加上该应用是EurekaClient的注解@EnableDiscoveryClient,并且按SpringBoot的方式写启动类。
//表示该应用是EurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
新建配置文件,生产者的端口号以8开头,配置注册服务中心地址,并设置应用程序的名称。
server:
port: 8001
eureka:
client:
service-url:
#注册服务中心地址
defaultZone: http://localhost:7000/eureka/
#指定应用程序的名称
spring:
application:
name: provider
此时需要先启动Server,再启动Client。再次访问Eureka,其中显示同一名称下有三个路径。
红字提醒:
THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
自我保护机制被关闭,这将导致在网络或者其他异常发生的情况下不会保护过期的实例
RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
续约阀值过低,自我保护机制被关闭,这将导致在网络或者其他异常发生的情况下不会保护过期的实例
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
紧急!因续约阀值过低,Eureka自我保护机制已启动,为安全起见,可能导致一些本已过期的实例不会被剔除注册中心
3.5 消费者Custmer依赖和配置类、启动类和配置文件
加入web依赖和client依赖,为了实现负载均衡,所以加入loadbalancer负载均衡依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--使用负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
新建配置类,使用RestTemplate的方法,默认为轮询方法。
@Configuration
public class RestConfig {
@Bean
@LoadBalanced//默认轮询方式
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
启动类中,加上该应用是EurekaClient的注解@EnableDiscoveryClient,并且按SpringBoot的方式写启动类。
//表示该应用是EurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaCustomerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaCustomerApplication.class, args);
}
}
新建配置文件,消费者的端口号以9开头,配置注册服务中心地址,并设置应用程序的名称。
server:
port: 9000
eureka:
client:
service-url:
#注册服务中心地址
defaultZone: http://localhost:7000/eureka/
#指定应用程序的名称
spring:
application:
name: customer
新建controller文件,取出配置类中的RestTemplate,设置取出所有路径下的文件,并设置返回值类型。
@RestController
public class MyController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/test")
public String test(){
//取出PROVIDER所有文件,返回String型
return restTemplate.getForObject("http://PROVIDER/test",String.class);
}
}
四.心跳
在启动时,如果先启动Client将会报错,此时再启动Server,Client会自动恢复。
在Clien启动的时候,会有一个心跳线程,保证默认每隔30秒的时候向Server发送一个心跳,告诉Server当前的Client还存活着。(心跳)
Server在接收到请求之后,先去自己的注册表中去,找到请求的对应的服务信息,在这个服务信息里面有个Lease的对象,更新Lease对象里面的LastUpdateTimestamp时间戳,每一次接收到都会更新这个时间戳。(Renew 续约)
Server会每60秒遍历一次注册表中的信息,然后查看注册表中的信息是否有过期的,如果90秒还没有更新对应的LastUpdateTimestamp就表示这个服务过期。
五.公共资源项目
可以新建一个项目专门放实体类,在其他项目中调用,这个实体类为公共项目。
5.1新建实体类
不需要启动文件,只需要在entity包下新建一个实体类。
@Data
public class NameEntity {
public String name;
public int age;
public String url;
}
在配置文件中加入lombok依赖。
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
配置文件中设置好name为customer
server:
port: 9000
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka/
spring:
application:
name: customer
5.2使用
在生产者的项目中使用,直接new实体类对象,会在pom文件中新增一个实体类的jar包,最后在消费者中将返回值设为实体类类型。
生产者使用实体类项目
@RestController
public class MyController {
@Value("${server.port}")
public String url;
@RequestMapping("/test")
public NameEntity test(){
NameEntity nameEntity=new NameEntity();
nameEntity.setName("sean1");
nameEntity.setAge(21);
nameEntity.setUrl(url);
return nameEntity;
}
}
生产者pom文件,凡是使用实体类项目,都需要加jar包
<dependencies>
...
<!--实体类的jar包-->
<dependency>
<groupId>org.example.entity</groupId>
<artifactId>eurekaEntity</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
消费者使用负载均衡
@RestController
public class MyController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/test")
public NameEntity test() {
return restTemplate.getForObject("http://PROVIDER/test", NameEntity.class);
}
}
5.3效果
显示对象,并且会轮询生产者。
六.公共依赖
例如热启动等依赖,所有项目都需要使用,在每个项目中都单独添加比较繁琐,一旦项目过多,需要添加多次,且不好管理。像这种多个项目都需要使用的,可以将依赖加到父项目中,module中所有项目都可以使用热启动。
<!--管理依赖的版本号-->
<dependencyManagement>
...
</dependencyManagement>
<!--公共依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
但是只需要生产者使用的,或者使用项目少于三分之二的依赖,就需谨慎思考需不需要加入父项目中。例如,数据库依赖、mybatis依赖、 thymeleaf依赖、lombok依赖等。
七.页面和前后端数据传输(restTemplate)
前端传值
后端传值
7.1页面
首先页面需要在消费者项目中,所以controller中需要有登录页面。
@ResponseBody 返回值为对象,@Controller注释下需要返回对象时使用。@ResponseBody+@Controller=@RestController
@Controller
public class MyController {
@Autowired
RestTemplate restTemplate;
@ResponseBody
@RequestMapping("/test")
public NameEntity test() {
return restTemplate.getForObject("http://PROVIDER/test", NameEntity.class);
}
@RequestMapping("/gotoLogin")
public String gotoLogin(){
return "/login";
}
}
页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form th:action="@{/gotologin}">
<span></span>
<input type="text" class="am-radius" placeholder="请输入用户名" name="username" />
<input type="password" placeholder="请输入密码" name="password" />
<input type="submit" th:value="登录"/>
</form>
</body>
</html>
效果
7.2后端向页面放值
后端使用ModelAndView向页面放值,页面显示。
@RequestMapping("/gotoLogin")
public ModelAndView gotoLogin(){
ModelAndView mav = new ModelAndView();
NameEntity forObject = restTemplate.getForObject("http://PROVIDER/test", NameEntity.class);
mav.addObject("test",forObject.getName());
mav.setViewName("login");
return mav;
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form>
<span></span>
<input type="username" th:placeholder="请输入用户名" th:value="${test}" name="username"/>
<input type="password" placeholder="请输入密码" name="password" />
<input type="submit" th:value="登录"/>
</form>
</body>
</html>
7.3页面向后端提交
页面输入用户名和密码,提交到后端消费者,消费者传到生产者后端在进行判断,返回登录成功或登录失败到消费者,消费者再放到页面上进行显示。
restTemplate.getForObject 向远程取数据 两个参数url和数据类型
restTemplate.postForObject 向远程服务传参数 三个参数url、参数、返回值类型
@RequestBody 跨域(端口不同)得到对象数据,必须是post提交
页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form th:action="@{/loginEntity}" method="post">
<span th:text="${result}"></span>
<input type="username" th:placeholder="请输入用户名" name="username"/>
<input type="password" placeholder="请输入密码" name="password" />
<input type="submit" th:value="登录"/>
</form>
</body>
</html>
消费者
@Controller
public class MyController {
@Autowired
RestTemplate restTemplate;
@Autowired
DiscoveryClient discoveryClient;
@ResponseBody
@RequestMapping("/test")
public NameEntity test() {
return restTemplate.getForObject("http://PROVIDER/test", NameEntity.class);
}
@RequestMapping("/gotoLogin")
public String gotoLogin(){
return "login";
}
@RequestMapping("/loginEntity")
public ModelAndView loginEntity(User user) {
ModelAndView mav = new ModelAndView();
String result = restTemplate.postForObject("http://PROVIDER/loginProvider", user, String.class);
mav.addObject("result",result);
mav.setViewName("login");
return mav;
}
}
因为使用负载均衡,所以不知道第一次回去那个生产者,所以每个生产者都需要写loginProvider。
@RestController
public class MyController {
@Value("${server.port}")
public String url;
NameEntity nameEntity = new NameEntity();
@RequestMapping("/test")
public NameEntity test() {
nameEntity.setName("sean1");
nameEntity.setAge(21);
nameEntity.setUrl(url);
return nameEntity;
}
@RequestMapping("/loginProvider")
public String loginProvider(@RequestBody User user) {
if (user.getUsername().equals("admin") && user.getPassword().equals("123456")) {
return "登录成功";
} else {
return "登录失败";
}
}
}
效果
进入gotoLogin页面
输入用户名密码后:
首先提交到消费者,消费者使用postForObject向生产者传值并接收并提供返回值。
进入生产者做处理,并返回登录成功或失败
最后消费者收到并返回页面
登录失败
登录成功