服务注册与发现

微服务基本组成

Eureka

  • 服务治理:管理服务与服务之间的依赖关系,可以实现服务调用、负载均衡、容错等,实现服务注册与发现
  • 服务注册与发现Eureka采用了CS的设计架构,EurekaServer作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用心跳连接,通过监控EurekaServer就可以监控各个微服务是否正常

Eureka和Dubbo架构图

EurekaServer

  • 提供服务注册服务
  • 各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样在服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到

EurekaClient

  • 通过注册中心进行访问
  • 是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向EurekaServer发生心跳(默认周期为30秒)
  • 如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除

单机版Eureka构建

server端

  • 引入依赖
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  • 配置yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 7001
eureka:
instance:
# eureka服务端的实例名称
hostname: localhost
client:
# false表示不用注册自己
register-with-eureka: false
# false表示自己就是注册中心,不需要去检索服务
fetch-registry: false
service-url:
# 设置与EurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  • 配置启动类
1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}

client端

  • 引入依赖
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  • 配置yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 8001
spring:
application:
# 这个必须得配置,用来区别不同的微服务名称
name: cloud-payment-service
eureka:
client:
# 将自己注册到EurekaServer端
register-with-eureka: true
# 是否从EurekaServer抓取已经注册的信息,默认为true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
  • 配置启动类
1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}

consumer

1
2
3
4
5
6
7
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/consumer/payment")
@Slf4j
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";

@Autowired
private RestTemplate restTemplate;

@GetMapping("/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}

@GetMapping("/select/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/select/" + id, CommonResult.class);
}
}

集群版Eureka构建

server端

只需要修改yml配置文件即可

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 7001
eureka:
instance:
# 这里使用域名
hostname: eureka7001.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
# 注册另一个EurekaServer
defaultZone: http://eureka7002.com:7002/eureka/
1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 7002
eureka:
instance:
# 这里使用域名
hostname: eureka7002.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
# 注册另一个EurekaServer
defaultZone: http://eureka7001.com:7001/eureka/

client端

注册多个server端

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
# 多个server
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

consumer

1
2
3
4
5
6
7
8
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced // 提供RestTemplate负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/consumer/payment")
@Slf4j
public class OrderController {
// 这里不能写死了,写的地址是注册在Eureka上的服务名称
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Autowired
private RestTemplate restTemplate;

@GetMapping("/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}

@GetMapping("/select/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/select/" + id, CommonResult.class);
}
}

服务名称和ip显示

在client端配置

1
2
3
4
5
6
eureka:
instance:
# 显示服务名
instance-id: payment8001
# 显示ip
prefer-ip-address: true

Discovery

对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息

  • 主启动类开启
1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableEurekaClient
// 开启Discovery
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.cloud.client.discovery.DiscoveryClient;
/**
* 使用 discoveryClient监控微服务
*/
@Resource
private DiscoveryClient discoveryClient;

@GetMapping("/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
for (String element : services) {
// 这里打印出所有注册在Eureka中的微服务名称
log.info("***** element:"+element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
// 打印出服务名称,服务器地址,端口,uri
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return discoveryClient;
}

Eureka自我保护

Eureka自我保护

  • 某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
  • 属于CAP里面的AP分支

关闭自我保护

Eureka关闭自我保护

  • server端配置
1
2
3
4
5
6
eureka:
server:
# 关闭Eureka自我保护机制,保证不可用的服务被即时剔除
enable-self-preservation: false
# 间隔时间改为2秒钟,超时剔除服务
eviction-interval-timer-in-ms: 2000
  • client端配置
1
2
3
4
5
6
eureka:
instance:
# Eureka服务端在收到最后一次心跳后等待时间上线,单位是秒(默认90秒),超时剔除服务
lease-expiration-duration-in-seconds: 2
# Eureka客户端向服务端发送心跳的间隔时间,单位是秒(默认是30秒)
lease-renewal-interval-in-seconds: 1

Zookeeper

引入依赖

1
2
3
4
5
6
7
8
<!--
自带的zookeeper服务是3.5版本的,
如果zookeeper使用的是3.4就需要排除jar包,重新手动添加
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>

配置yml

1
2
3
4
5
6
7
spring:
application:
name: cloud-provider-payment
cloud:
# 注册到zookeeper注册中心
zookeeper:
connect-string: 192.168.0.13:2181

编写启动类

1
2
3
4
5
6
7
8
@SpringBootApplication
// 该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class, args);
}
}

Consul

docker安装

1
2
3
4
5
6
7
8
9
10
# 下载
docker pull consul
# 创建指定文件夹
mkdir -p /data/consul/{conf,data}
# 创建容器
docker run --name consul -p 8500:8500 -v /data/consul/conf/:/consul/conf/ -v /data/consul/data/:/consul/data/ -d consul
# 启动
docker start consul
# 访问
http://192.168.0.13:8500

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

配置yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 8006

spring:
application:
name: consul-provider-payment
cloud:
# consul配置
consul:
host: 192.168.0.13
port: 8500
discovery:
service-name: ${spring.application.name}
# 不是一台主机,下面这两个都要配,ip是当前主机的ip,端口是当前服务的端口
health-check-url: http://192.168.0.14:8006/actuator/health
hostname: 192.168.0.14

编写启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentConsul8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentConsul8006.class,args);
}
}

异同点

  • CAP:C:Consistency(强一致性),A:Availability(可用性),P:Partition tolerance(分区容错)
  • CAP理论关注粒度是数据,而不是整体系统设计的策略
组件 语言 CAP 服务健康检查 对外暴露接口 SpringCloud集成
Eureka JAVA AP 可配支持 HTTP 已集成
Consul GO CP 支持 HTTP/DNS 已集成
Zookeeper JAVA CP 支持 客户端 已集成

AP架构

  • 当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统可用性
  • 结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

AP架构

CP架构

  • 当网络分区出现后,为了保证一致性,就必须拒绝连接请求
  • 结论:违背了可用性A的要求,只满足一致性和分区容错,即CP

CP架构

相关文章

SpringCloud

服务调用

SpringCloud-GateWay工具类

SpringCloud-OpenFeign问题

SpringQuartz动态定时任务

DockerCompose常用软件配置

Redis集群搭建

redis分布式锁

服务链路追踪

K8S