Eureka
了解注册中心的一些基本概念有助于理解思想,编程不是单纯的编码,更多的要思考内在的逻辑,编程能力才能更快的提升。
1.基础知识
- 服务治理:传统RPC远程调用框架中管理服务与服务之间的调用比较复杂,所以使用服务治理来管理服务与服务之间的依赖关系,实现服务调用,负载均衡、容错等,实现服务的发现与注册。
- 服务注册与发现:启用一个注册中心,用于管理不同服务的信息,比如别名,通信地址。服务提供放将服务以别名的方式注册到注册中心之后,服务的订阅放通过该信息获取实际的服务地址,然后实现RPC调用。
- Eureka 的两个组件
- Eureka Server 提供服务注册
- EurekaClient通过注册中心访问
2.单机Eureka构建
1.eurekaServer端服务注册中心
1.建Module
cloud-eureka-server7001
2.改POM
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--boot web actuator-->
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
编写Yml文件
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/
编写启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001
{
public static void main(String[] args)
{
SpringApplication.run(EurekaMain7001.class,args);
}
}
测试
http://localhost:7001/
2.EurekaServer端cloud-provider-payment8001
1.建Module
cloud-provider-payment8001
2.改POM
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</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.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.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文件
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities # 所有Entity别名类所在包
编写启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001
{
public static void main(String[] args)
{
SpringApplication.run(PaymentMain8001.class,args);
}
}
业务类Controller层 参考上一章
在这里插入代码片
测试
先启动EurakaServer
再启动EurakaClient
http://localhost:7001/
3.EurekaServer端cloud-consumer-order80
1.建Module
cloud-consumer-order80
2.改POM
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</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文件
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
编写启动类
@SpringBootApplication
@EnableEurekaClient
public class OrderMain80
{
public static void main(String[] args)
{
SpringApplication.run(OrderMain80.class,args);
}
}
业务类Controller层
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id")Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
测试
先启动EurakaServer
再启动Provider 8001
最后启动Order80
http://localhost:7001/
可以看到服务都成功注册到服务中心
http://localhost/consumer/payment/get/31
在数据库中查找相关的数据库
可以自己建立一个相关的数据库手动向其中插入数据,过程比较简单不在这里赘述
注意:application.yml中的配置文件是严格控制缩进的。
2.集群Eureka构建
1.原理
服务注册:将服务信息注册进入注册中心
服务发现:从服务注册中心上获取服务信息
实质:存key服务名 取key value调用地址
1.启动Eureka注册中心
2.启动服务提供者Payment支付服务
3.支付服务启动后会把自身信息(比如服务地址以别名方式注册进Eureka
4.消费者Order服务在需要调用接口时,使用服务别名去注册中心获取实际的Rpc调用地址
5.消费者获取调用地址后,底层实际是用HtpClient技术实现远程调用
6.消费者获得服务地址后会缓存在本地Jvm内存中,默认没30秒更新服务调用地址
集群的构建多了服务器之间的负载均衡
注册中心集群
参考cloud-eureka-server7001构建cloud-eureka-server7002
修改hosts文件映射 添加
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
修yml配置文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
// 7002 配置文件
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
主启动
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002
{
public static void main(String[] args)
{
SpringApplication.run(EurekaMain7002.class,args);
}
}
将支付微服务发布到Eureka集群配置中
#修改
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
将订单微服务80,发布到Eureka集群配置中
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
测试
1.启动Eureka服务注册中心:7001,7002
2.启动服务提供者8001
3.服务消费者80
访问
http://localhost/consumer/payment/get/31
服务提供者集群
参考创建cloud-provider-payment8002
修改pom文件
server:
port: 8002
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
#defaultZone: http://localhost:7001/eureka # 单机版
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities # 所有Entity别名类所在包
修改Contorller
@RestController
@Slf4j
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
@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,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment);
}else{
return new CommonResult(444,"插入失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
{
Payment payment = paymentService.getPaymentById(id);
log.info("*****查询结果:{}",payment);
if (payment != null) {
return new CommonResult(200,"查询成功"+"\t 服务端口:"+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
}
注意!
访问地址不要写成固定的形式,否则无法使用负载均衡功能
// 通过在eureka上注册过的微服务名称调用
public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";
使用@LoadBalaced注解赋予RestTemplate负载均衡的能力
@Configuration
public class ApplicationContextBean
{
@Bean
@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
启动顺序
1.Eureka服务注册中心 7001 7002
2.服务提供者8001 8002
3.服务消费者 80
结果: 可以直接调用服务不用关心地址和端口号,而且还具有负载功能。
附
###actuator信息完善
将在Eureka中显示的服务器信息修改
修改部分
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
#defaultZone: http://localhost:7001/eureka # 单机版
instance:
instance-id: payment8001
服务发现
对于注册进eureka里面的微服务可以通过服务发现来获得该服务的信息
1.修改cloud-provider-payment8001的Controller
添加:
@GetMapping(value = "/payment/discovery")
public Object discovery()
{
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
在主启动类上添加
@EnableDiscoveryClient
访问对应的地址可以获得服务的信息
Eureka 的自我保护
Eureka 数据CAP模型中的AP 保证高可用而不强调数据一致性
使用设置可以关闭
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
#defaultZone: http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
defaultZone: http://eureka7001.com:7001/eureka
server:
#关闭自我保护机制,保证不可用服务被及时踢除
enable-self-preservation: false