提示:本文包括单机Eureka构建、集群Eureka构建、微服务提供者集群配置。
常用的服务注册中心
服务注册中心 | 使用情况 |
---|---|
Eureka | ×停止维护,可使用 |
Zookeeper | √ |
Consul | √ |
Nacos | √(常用) |
Eureka简介
-
Spring Cloud 下的 Netflix Eureka 组件项目宣布停止开发了,但是我们还是有必要学习一下Eureka,这样会加深对服务注册中心的理解,也更加方便学习后面的服务注册组件。
-
服务治理: Spring Cloud封装了Netflix 公司开发的Eureka模块来实现服务治理。
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册
。 -
什么是服务注册与发现?
Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka的客户峨连接到Eureka seve并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比加如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地NAPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址)
-
Eureka包含两个组件:Eureka Server和Eureka Client
(1)Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
(2)EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
Eureka的使用
- 延用博客【SpringCloud】IDEA如何创建一个SpringCloud项目中的订单项目,原博客是实现的是提供者直接向消费者提供服务,现在使用Eureka,通过注册中心的方式提供服务。
- 在之前博客的项目下新建一个Module为普通的Maven项目cloud-eureka-server7001
- 依赖pom.xml
<?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">
<parent>
<artifactId>SpringcloudTest</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
- 在resource目录下新建一个application.yml配置文件【设置端口、eureka配置】
server:
port: 7001
eureka:
instance:
hostname: localhost # eureka 服务器的实例名称
client:
# false 代表不向服务注册中心注册自己,因为它本身就是服务中心
register-with-eureka: false
# false 代表自己就是服务注册中心,自己的作用就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置与 Eureka Server 交互的地址,查询服务 和 注册服务都依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- SpringBoot的启动类,在在src/main/java下新建包名为com.gaolang,然后创建一个EurekaServerMain7001类
// exclude :启动时不启用 DataSource的自动配置检查
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaServer // 表示它是服务注册中心
public class EurekaServerMain7001 {
public static void main(String[] args){
SpringApplication.run(EurekaServerMain7001.class, args);
}
}
运行启动类进行测试:运行后,访问:http://localhost:7001/,出现下图界面,表示成功,接下来EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider
和EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer
注册
cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider
cloud-provider-payment8001模块中:
- 在cloud-provider-payment8001Module中添加eureka-client端依赖pom.xml
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 添加eureka配置信息 applicatiom.yml,注意无需修改之前的配置信息,直接添加即可
eureka:
client:
# 注册进 Eureka 的服务中心
register-with-eureka: true
# 检索 服务中心 的其它服务
fetch-registry: true
service-url:
# 设置与 Eureka Server 交互的地址
defaultZone: http://localhost:7001/eureka/
- SpringBoot启动类添加
@EnableEurekaClient
注解 - 启动cloud-provider-payment8001,访问:http://localhost:7001/,出现下图界面,发现已注册成功。
cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer
cloud-consumer-order80模块中:
- 在cloud-consumer-order80 Module中添加eureka-client端依赖pom.xml
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 添加eureka配置信息 applicatiom.yml,注意无需修改之前的配置信息,直接添加即可
spring:
application:
name: cloud-order-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
- SpringBoot启动类添加
@EnableEurekaClient
注解 - 启动cloud-consumer-order80,访问:http://localhost:7001/,会发现有两个微服务
小结
到此为止,单机的Eureka构建完成,但是一旦它出现故障,会导致整个服务环境不可用,解决办法搭建Eureka注册中心集群,实现负载均衡和故障容错。
集群Eureka构建
-
Eureka集群原理:
-
多个Eureka服务遵守的原则:互相注册,相互守望。
-
接下来保留上面单机的Eureka,创建一个新的Module:cloud-eureka-server7002,依赖pom.xml和上面的相同。
-
找到C:\Windows\System32\drivers\etc路径下的hosts文件,添加
##SpringCloud##
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
不同的端口号映射同一个地址。
5. 修改cloud-eureka-server7001中的配置文件:apllication.yml
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名字
client:
register-with-eureka: false #表识不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/ #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
注意hostname
和defaultZone
,其他的eureka服务都需要注册到defaultZone,用逗号 隔开,互相注册,相互守望。
- cloud-eureka-server7002中的配置文件application.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/ #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
- SpringBoot启动类:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaServer
public class EurekaMain7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7002.class,args);
}
}
-
测试: 启动两个eureka服务,分别访问:http://localhost:7001/和http://localhost:7002/,都可以访问之后,再访问http://eureka7001.com:7001/eureka/和http://eureka7002.com:7002/eureka/,如果这里不能访问基本上就是host文件没有生效,建议重启项目,或者,重启电脑。到这里集群配置完成。
-
将服务提供者8001微服务发布到上面2台Eureka集群配置中
修改8001中的配置文件application.yml中的 eureka配置
eureka:
client:
# 注册进 Eureka 的服务中心
register-with-eureka: true
# 检索 服务中心 的其它服务
fetch-registry: true
service-url:
# 设置与 Eureka Server 交互的地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
- 将服务消费者80微服务发布到上面2台Eureka集群配置中
修改80中的配置文件application.yml中的 eureka配置
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版
11.测试: 启动4个项目 7001,7002,8001,80,然后进行测试分别访问http://eureka7001.com:7001/eureka/和http://eureka7002.com:7002/eureka/看提供者和消费者是否注册成功。
最后测试http://localhost/customer/payment/1
到这,集群配置完成。
微服务提供者集群配置
- 服务提供者一般都是有多个,防止单个出现故障后,后面不能提供服务,也可以看到官网的图片,Eureka Server和Server Provier都是集群
- 也就是说我们要将上面的单个服务提供者变成多个,即下图:
- 正式开始创建下一个Module,cloud-provider-payment8002。
- pom.xml和8001一样:
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</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>
- application.yml和8001一样,只是需要修改端口号
server:
port: 8002
spring:
application:
name: cloud-provider-payment8001
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
eureka:
client:
# 注册进 Eureka 的服务中心
register-with-eureka: true
# 检索 服务中心 的其它服务
fetch-registry: true
service-url:
# 设置与 Eureka Server 交互的地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.gaolang.entities # 所有Entity 别名类所在包
-
业务类和主启动类直接复制8001的,不过记得修改主启动类的名字,还有8001下resource目录下的mapper目录记得copy。【从哪里copy,参考博客:【SpringCloud】IDEA如何创建一个SpringCloud项目】
-
我们要清楚,微服务消费者是从哪一个提供者上拿的信息,那么就需要修改8001和8002的controller层的PaymentController类【8002是从8001copy的,也需要修改】
@RestController
@Slf4j
public class PaymentController {
@Autowired
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("====插入结果: "+result);
if (result > 0){
return new CommonResult(200,"数据库插入成功,serverPort:"+serverPort,result);
}else {
return new CommonResult(444,"数据库插入失败",null);
}
}
@GetMapping(value = "/payment/{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);
}
return new CommonResult(444, "没有对应id的记录", null);
}
}
- 修改80消费中的OrderController 类,之前
PAYMENT_URL
写死了为8001端口,现在改成eureka中提供者的名字:CLOUD-PROVIDER-PAYMENT8001,注意这里并不是只有8001一个端口,代表后面的两个端口。
@RestController
@Slf4j
public class OrderController {
public static final String PAYMENT_URL = "http://CLOUD-PROVIDER-PAYMENT8001";
@Autowired
private RestTemplate restTemplate;
@PostMapping("customer/payment/create")
public CommonResult<Payment> create (Payment payment){
/**
param1 请求地址,param2 请求参数, param3 返回类型
*/
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}
@GetMapping("customer/payment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id")Long id){
return restTemplate.getForObject(PAYMENT_URL + "/payment/" + id, CommonResult.class);
}
}
- 同时也需要修改80下config层 ApplicationContextConfig类,加上
@LoadBalanced
注解赋予RestTemplate负载均衡的能力。【这里属于Ribbon的负载均衡功能,轮流】
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 测试: 先后启动7001,7002,8001,8002,80五个项目
(1)访问http://eureka7001.com:7001/eureka/和http://eureka7002.com:7002/eureka/,可以看到有两个服务提供者。
(2)访问http://localhost/customer/payment/1,可以看到来自8001 - 小结