SpringCloud
五、Eureka服务注册与发现
Eureka基础知识
服务治理
服务注册
Eureka两组件
单机Eureka构建步骤
IDEA生成eurekaServer端服务注册中心
1. 建module
cloud-eureka-server7001
2. 改pom
server端依赖对比:
在pom中添加
<dependencies>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--引入自定义的api通用包,可以使用Payment支付bean-->
<dependency>
<groupId>com.achang.springcloud</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 一般通用配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3. 写yml
在resources目录下新建application.yml文件
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己(想注册也可以,不过没必要)
register-with-eureka: false
#false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/
4. 主启动
在java包下新建com.achang.springcloud.EurekaMain7001
@SpringBootApplication
@EnableEurekaServer //表示此项目是eureka的服务注册中心
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
5. 测试
启动项目,在浏览器输入http://localhost:7001/
将EurekaClient端8001注册进EurekaServer成为服务提供者provider
client端依赖对比
-
引入依赖
<!-- eureka-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
在yml文件中添加
eureka:
client:
#true表示向注册中心注册自己,默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
3.在主配置类上加上@EnableEurekaClient
注解,表示这个项目是eureka的客户端。
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {
// 主启动类
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
4.启动项目,然后刷新页面,成功注册进注册中心。
在yml文件中application.name就是注册进注册中心时的应用名。
将EurekaClient端80注册进EurekaServer成为服务消费者consumer
1、引入依赖
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、在yml文件中添加
spring:
application:
name: cloud-order-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
3、在主配置类上加上@EnableEurekaClient
注解。
@SpringBootApplication
@EnableEurekaClient
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
4、启动项目,刷新页面
启动7001-8001-80
http://localhost/consumer/payment/add?serial=%E5%88%98%E9%A3%9E
http://localhost/consumer/payment/get/1
集群Eureka构建步骤
原理说明
搭建Eureka注册中心集群,实现负载均衡+故障容错。
Eureka集群:相互注册,相互守望。
构建eurekaServer集群环境
1、参照cloud-eureka-server7001新建cloud-eureka-server7002和cloud-eureka-server7003。
2、Windows系统的兄弟就跟着老师修改hosts文件,C:\Windows\System32\drivers\etc
在最后一行加入:保存退出
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
3、修改7001的yml文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
# 单机
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群版 相互注册,相互守望
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
# defaultZone是固定写法,如果想自定义,需要按以下写法才行:
# region: eureka-server
# availability-zones:
# eureka-server: server1,server2
# service-url:
# server1: http://eureka7002.com:7002/eureka/
# server2: http://eureka7003.com:7003/eureka/
4、修改7002的yml文件
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
#集群版 相互注册,相互守望
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #相互注册,相互守望
5、修改7003的yml文件
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
#集群版 相互注册,相互守望
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #相互注册,相互守望
6、然后启动这三个项目
通过域名逐个测试访问:
http://eureka7001.com:7001/
http://eureka7002.com:7002/
http://eureka7003.com:7003/
将支付服务8001和订单服务80微服务发布到集群配置中
把两个项目的yml文件中的defaultZone改为:
#集群版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
启动5个项目进行测试:(先启动集群,再启动8001,最后启动80)
集群后台截图:
构建支付服务提供者集群环境
- 按照8001新建8002(只多建了一个提供者,建多了怕电脑受不了)。(除了要yml文件中需要改端口号和主配置类,其他直接复制8001的,yml文件中的应用名不需要改,因为是集群,所以应用名需要一致)
- 分别在所有的提供者的PaymentController中加入:(这个
@Value是spring的注解
)
@RestController
@Slf4j //日志
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
//前后端分离,所以不能直接返回对象,数据要先经过CommonResult封装再返回
@PostMapping(value = "/payment/add")
public CommonResult add(@RequestBody Payment payment){
int result = paymentService.add(payment);
log.info("-----插入结果----: "+ result);
if (result > 0){
//插入成功
return new CommonResult(200,"插入数据库成功 serverPort="+serverPort,result);
}else {
return new CommonResult(444,"插入数据库失败 serverPort="+serverPort,null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id")Long id){
Payment result = paymentService.getPaymentById(id);
log.info("-----查询结果----: "+ result);
if (result != null){
//查询成功
return new CommonResult(200,"查询成功 serverPort="+serverPort,result);
}else {
return new CommonResult(444,"没有对应记录,查询id: "+id+" ,serverPort="+serverPort,null);
}
}
}
3、修改消费者的OrderController,把写死的url改为服务名称:
public static final String PAYMENT_URL ="http://CLOUD-PAYMENT-SERVICE";
@RestController
@Slf4j
public class OrderController {
// public static final String PAYMENT_URL ="http://localhost:8001";
public static final String PAYMENT_URL ="http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/add")
public CommonResult<Payment> add(Payment payment){
log.info("********插入的数据:" + payment);
return restTemplate.postForObject(PAYMENT_URL+"/payment/add",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id")Long id){
log.info("********查询的id:" + id);
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
4、然后在消费者的ApplicationContextConfig里的restTemplate方法上加上@LoadBalanced
,开启负载均衡功能。
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
注:不加@LoadBalanced
,80不知道要去调用哪个CLOUD-PAYMENT-SERVICE,没有一种默认的机制,通过这个注解,给他一个默认的机制
5、启动eurekaServer集群,启动提供者集群,启动消费者。
如果启动提供者后出现,这个错误:Public Key Retrieval is not allowed
请在yml文件中的datasource.datasource.url后加上&allowPublicKeyRetrieval=true
即可解决。
另外两个eurekaserver也一样,就不截图。
在浏览器中输入http://localhost/consumer/payment/get/1
,多次刷新可以看到,提供服务的应用在不同的切换,实现负载均衡的效果。
自动实现的切换端口,负载均衡
actuator微服务信息完善
修改三个微服务的yml文件:
#######8001#######
# client:
# ... instance要和client对齐
instance:
instance-id: payment8001 # 修改显示的主机名
prefer-ip-address: true # 访问路径可以显示ip地址
#######8002#######
instance:
instance-id: payment8002 # 修改显示的主机名
prefer-ip-address: true # 访问路径可以显示ip地址
#######80#######
instance:
instance-id: consumer80 # 修改显示的主机名
prefer-ip-address: true # 访问路径可以显示ip地址
修改前:
修改后:
服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息。
修改提供者集群的controller
1、在主配置类上加上@EnableDiscoveryClient
注解,启用发现客户端。
@SpringBootApplication
@EnableEurekaClient
=========================
@EnableDiscoveryClient
=========================
public class PaymentMain8001 {
// 主启动类
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
2、在两个提供者的PaymentController中加入:
@RestController
@Slf4j //日志
public class PaymentController {
===================================================
//springframework的DiscoveryClient(不要导错包了)
@Resource
private DiscoveryClient discoveryClient;
==================================================
@GetMapping(value = "/payment/discovery")
public Object discovery(){
//遍历具体注册的微服务
List<String> services = discoveryClient.getServices();
for (String service:services){
log.info("**********service****: "+service);
}
//获取具体 CLOUD-PAYMENT-SERVICE 微服务的具体实例
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
//getServiceId服务器id getHost主机名称 getPort端口号 getUri地址
for (ServiceInstance s:instances){
log.info(s.getServiceId()+"\t"+s.getHost()+"\t"+s.getPort()+"\t"+s.getUri());
}
return this.discoveryClient;
}
}
测试
对8001进行测试
,在浏览器输入:http://localhost:8001/payment/discovery
左边:具体注册的微服务
右边:具体某个微服务的具体实例
Eureka自我保护
概述
导致原因
只有在一定时间内丢失大量服务的心跳才开启自我保护模式。
禁止自我保护
先把cloud-eureka-server7001和cloud-provider-payment8001都切回单机版测试禁止自我保护。
cloud-eureka-server7001的yml文件:
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
#单机
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/
#集群版 相互注册,相互守望
# defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
server:
#关闭自我保护,默认为true
enable-self-preservation: false
#心跳的间隔时间,单位毫秒
eviction-interval-timer-in-ms: 2000
cloud-provider-payment8001的yml文件:
eureka:
instance:
#Eureka客户端向服务端发送心跳的时间间隔,单位秒(默认30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待的时间上限,单位秒(默认90秒),超时剔除服务
lease-expiration-duration-in-seconds: 2
启动注册中心和提供者:
然后关闭提供者(模拟网络延时),提供者直接被剔除。
六、Zookeeper服务注册与发现
注册中心Zookeeper
关闭linux的防火墙:
systemctl stop firewalld
systemctl status firewalld
进入到zookeeper的bin目录下启动zkServer.sh
或者通过docker来启动zookeeper
#拉取Zookeeper镜像
docker pull zookeeper
#启动Zookeeper
docker run --name zk01 -p 2181:2181 --restart always -d zookeeper
服务节点是临时节点还是持久节点?
zookeeper也是有心跳机制,在一定时间能如果一直没心跳返回,Zookeeper就会把服务节点剔除掉。所以在Zookeeper上的服务节点是临时节点。
服务提供者
为了区分,中间空个8003。
1、新建工程cloud-provider-payment8004
2、pom
<!--因为接下来不会用到数据库,所以不导入数据库相关的依赖(防止没配置而报错)-->
<!--替换掉eureka依赖,其他直接复制8001-->
<!--SpringBoot整合Zookeeper客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<!-- 先排除自带的zookeeper3.5.3-->
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加zookeeper3.4.10版本(引入对应版本的依赖)-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
3、yml
#端口号
server:
port: 8004
spring:
application:
#服务别名——注册到zookeeper注册中心的名称
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 192.168.109.101:2181 #linux的ip加暴露的端口号
4、主启动类
@EnableDiscoveryClient //该注解用于向使用consul或者Zookeeper作为注册中心时注册服务
@SpringBootApplication
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class, args);
}
}
5、controller
新建controller.PaymentController
@Slf4j
@RestController
public class PaymentController {
@Value("${server.port}") //获取端口号
private String serverPort;
@RequestMapping("/payment/zk")
public String paymentzk(){
return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
}
}
12345678910111213
6、Zookeeper容器-------【docker的方式】
默认已经帮我们启动了服务端和客户端,如果想进去容器的话,使用下面的命令。
#查看正在运行的容器(查看Zookeeper容器的id)
docker ps
#进入zookeeper容器
docker exec -it 容器ID /bin/bash
#退出容器(或者按快捷键ctrl+P+Q退出)
exit
#启动容器
docker start 容器ID
#关闭容器
docker stop 容器ID
方式跟上面一样,进入到bin目录,通过./zkServer.sh start
启动每台的zookeeper服务,然后通过./zkCli.sh
来启动客户端
7、启动8004项目,浏览器输入http://localhost:8004/payment/zk
8、进入Zookeeper容器(成功注册进注册中心)
https://tool.lu/json/
服务消费者
-
新建消费者模块cloud-consumerzk-order80。
-
pom和yml直接复制8004。(yml中端口号改为80,应用名改为cloud-consumer-order,其他都相同)
-
主启动类。(与8004相同)
@SpringBootApplication @EnableDiscoveryClient public class OrderZKMain80 { public static void main(String[] args) { SpringApplication.run(OrderZKMain80.class,args); } }
-
在springcloud包下新建config.ApplicationContextConfig
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced //负载均衡 public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
-
新建controller.OrderZKController
@RestController @Slf4j public class ZKController { public static final String INVOKE_URL="http://cloud-provider-payment"; @Resource private RestTemplate restTemplate; @GetMapping("/consumer/payment/zk") public String paymentInfo(){ String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class); return result; } }
-
启动项目
http://localhost/consumer/payment/zk
七、Consul服务注册与发现
Consul官网:https://www.consul.io/
Consul中文文档:https://www.springcloud.cc/spring-cloud-consul.html
简介
Consul是一种服务网格解决方案,提供具有服务发现,配置和分段功能的全功能控制平面。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建完整的服务网格。Consul需要一个数据平面,并支持代理和本机集成模型。Consul附带了一个简单的内置代理,因此一切都可以直接使用,还支持Envoy等第三方代理集成。
主要特点
- 服务发现:Consul的客户端可以注册服务,例如 api或mysql,其他客户端可以使用Consul来发现给定服务的提供者。使用DNS或HTTP,应用程序可以轻松找到它们依赖的服务。
- 健康检测:领事客户端可以提供任意数量的运行状况检查,这些检查可以与给定服务(“ Web服务器是否返回200 OK”)或本地节点(“内存利用率低于90%”)相关。操作员可以使用此信息来监视群集的运行状况,服务发现组件可以使用此信息将流量从不正常的主机发送出去。
- KV存储:应用程序可以将Consul的分层键/值存储用于多种目的,包括动态配置,功能标记,协调,领导者选举等。简单的HTTP API使其易于使用。
- 安全的服务通信:领事可以为服务生成并分发TLS证书,以建立相互TLS连接。 意图 可用于定义允许哪些服务进行通信。可以使用可以实时更改的意图轻松管理服务分段,而不必使用复杂的网络拓扑和静态防火墙规则。
- 多数据中心:Consul开箱即用地支持多个数据中心。这意味着Consul的用户不必担心会构建其他抽象层以扩展到多个区域。
Consul旨在对DevOps社区和应用程序开发人员友好,使其非常适合现代,灵活的基础架构。
在docker上安装启动consul
#拉取consul镜像
docker pull consul
#启动consul
docker run -d -p 8500:8500/tcp --name myConsul consul agent -server -ui -bootstrap-expect=1 -client=0.0.0.0
然后在浏览器输入ttp://30.211.53.17/:8500
(linux的IP地址加上冒号8500)
服务提供者
1、新建服务提供者cloud-provider-consul-payment8006。
2、pom复制8004。(用下面的依赖替换Zookeeper的依赖)
<!--SpringCloud consul-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
3、yml
# consul服务端口号
server:
port: 8005
spring:
application:
name: consul-provider-payment
cloud:
# consul注册地址
consul:
host: 47.110.247.184 #用linux的ip地址(consul在本机就填localhost)
port: 8500
discovery:
service-name: ${
spring.application.name}
# 出掉去前端页面查看时爆红叉
heartbeat:
enabled: true
4、主启动类(与8004相同)
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8005 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8005.class,args);
}
}
5、controller
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}") //获取端口号
private String serverPort;
@RequestMapping("/payment/consul")
public String paymentConsul(){
return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
}
}
6、启动项目
http://localhost:8005/payment/consul
服务消费者
-
新建模块cloud-consumer-consul-order80
-
pom(与8006相同)
<dependencies> <!--SpringCloud consul-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <!--引入自定义的api通用包,可以使用Payment支付bean--> <dependency> <groupId>com.achang.springcloud</groupId> <artifactId>cloud-api-common</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--监控--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 一般通用配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
yml(端口号为80,应用名为consul-consumer-order,其他和8006相同)
server: port: 80 spring: application: name: cloud-consuml-order cloud: # consul注册地址 consul: host: 47.110.247.184 port: 8500 discovery: service-name: ${ spring.application.name} # 出掉去前端页面查看时爆红叉 heartbeat: enabled: true
-
主启动类(与8006相同)
@SpringBootApplication @EnableDiscoveryClient public class OrderConsulMain80 { public static void main(String[] args) { SpringApplication.run(OrderConsulMain80.class,args); } }
-
config(和zk的消费者相同)
@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
-
controller.OrderConsulController
@RestController @Slf4j public class OrderController { public static final String INVOKE_URL="http://consul-provider-payment"; @Resource RestTemplate restTemplate; @GetMapping(value = "/consumer/payment/consul") public String paymentInfo(){ String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class); return result; } }
-
启动项目
http://localhost/consumer/payment/consul
三者的异同点
CAP:(只能二选一)
A:可用性
C:一致性
P:分区容错性(微服务架构必须保证有P)
感谢尚硅谷
https://www.bilibili.com/video/BV18E411x7eT?p=36&spm_id_from=pageDriver
P15-P35内容