Eureka简介以及入门
1. Eureka简介
1.什么是服务治理
Springcloud封装了Netflix公司开发的Eureka来实现服务治理。
服务治理管理服务与服务之间的依赖关系,可以实现服务调用、负载均衡、容错等,实现服务注册于发现。
2.什么是服务注册与发现
Eureka采用CS架构。EurekaServer作为服务注册的服务器,它是注册中心。而系统中的其他服务,使用eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员可以通过eurekaserver来监控系统各个服务的运行情况。
在服务注册发现中,有一个注册中心。当服务器启动时会把当前服务器自己的信息,比如服务地址、通讯地址等以别名方式注册到注册中心。另一方(消费者),以别名的方式到注册中心获取到实际的地址,然后再实现本地的服务调用。
3.Eureka的两个组件
EurekaServer提供注册服务,各个微服务节点通过配置启动后,会在eureka服务中进行注册。这样EurekaServer服务注册表中将会存储所有可调用服务节点的信息,服务节点的信息可以在界面中直观的看到。
EurekaClient通过注册中心获取服务。是一个Java客户端,用于简化EurekaServer的交互;具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认30s)。如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从注册表中把这个服务节点移除(默认90s)。
2. Eureka基本使用
1. EurekaServer服务搭建-采用7001端口新建eureka单击版Server
1.新建moudle
2.写pom文件
<?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>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-eureka-server7001</artifactId> <dependencies> <!--eureka-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</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> </project>
3.编写yml配置文件
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机就是7001自己
defaultZone: http://localhost:7001/eureka/
4.编写主启动类(由于这个是作为Eureka的服务端,所以不需要其他业务类)
package cn.qz.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * @Author: qlq * @Description * @Date: 21:01 2020/10/2 */ @SpringBootApplication @EnableEurekaServer public class EurekaMain7001 { public static void main(String[] args) { SpringApplication.run(EurekaMain7001.class, args); } }
5.启动后访问本机7001端口进行测试:
2. 服务 cloud-provider-payment8081注册到eureka
1.pom文件增加如下EurekaClient客户端
<!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2.修改yml文件,增加eureka配置
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
3.主启动类增加如下注解
@EnableEurekaClient
4.启动服务访问eurekaserver的7001端口
注意需要先启动eurekaserver端的7001端口。
查看服务信息如图,这里的服务名称Application取的是服务注册方的spring.application.name,所以这里的服务名称一般要规范起名且不轻易修改。
3.订单服务注册饿到Eureka服务中心
1.pom文件增加如下客户端配置:
<!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2.修改yml配置,增加服务名称和eureka配置
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
3.主配置类增加eureka注解
@EnableEurekaClient
4. 修改RestTemplate,增加负载均衡配置
package cn.qz.cloud.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * 注入一个bean */ @Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
5.修改controller服务获取地址
package cn.qz.cloud.controller; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.utils.JSONResultUtil; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.List; import java.util.Map; /** * @Author: qlq * @Description * @Date: 22:09 2020/9/25 */ @RestController @RequestMapping("/consumer") public class OrderController { private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; @Resource private RestTemplate restTemplate; @PostMapping("/pay/save") public JSONResultUtil<Payment> save(@RequestBody Payment payment) { return restTemplate.postForObject(PAYMENT_URL + "/pay/save", payment, JSONResultUtil.class); } @GetMapping("/pay/listAll") public JSONResultUtil<List<Map<String, Object>>> listAll() { return restTemplate.getForObject(PAYMENT_URL + "/pay/listAll", JSONResultUtil.class); } }
6.启动服务测试即可:
$ curl http://localhost/consumer/pay/listAll % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:100 406 0 406 0 0 8638 0 --:--:-- --:--:-- --:--:-- 13096{"success":true,"code":"200","msg":"","data":[{"createtime":"2020-09-25T06:58:21.000+0000","serial":"测试","id":1},{"createtime":"2020-09-25T07:06:10.000+0000","serial":"测试","id":2},{"createtime":"2020-09-25T14:34:43.000+0000","serial":"测试1测试测试序列好23456","id":3},{"createtime":"2020-09-25T15:25:35.000+0000","serial":"测试1测试测试序列好23456555","id":1309514424784064514}]}
7.调用过程如下:
(1)启动Eureka注册中心
(2)启动服务提供者Payment支付服务,支付服务会把自身信息(比如服务地址)以别名方式注册到Eureka
(3)消费者Order调用接口时,实际是根据服务别名获取到实际地址,然后yongHttpClient实现接调用
(4)消费者获得服务信息后会缓存在JVM内存本地,默认30s更新一次服务调用地址
3.Eureka 集群
1.集群原理
简单说下,分布式环境有个CAP原则,又称为CAP理论,主要思想是在任何一个分布式系统中都无法同时满足CAP。
C(Consistency):表示一致性,所有的节点同一时间看到的是相同的数据。
A(Avaliablity):表示可用性,不管是否成功,确保一个请求都能接收到响应。
P(Partion Tolerance):分区容错性,系统任意分区后,在网络故障时,仍能操作。
Eureka集群的原理是多台机器相互守望,互相关注,和zookeeper不同的是不存在主节点的概念。所以相比于Zookeeper的选举主节点发生的处理中断(CP),Eureka满足的是AP原则(自我保护模式会保留异常的服务)。
2.集群搭建
2.1 搭建Eureka集群
0.配置两个虚拟主机
修改C:\Windows\System32\drivers\etc\hosts文件:
127.0.0.1 eureka1.com 127.0.0.1 eureka2.com
1.新建cloud-eureka-server7002与7001服务组成集群
2.修改pom
<?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>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-eureka-server7002</artifactId> <dependencies> <!--eureka-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</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> </project>
3.修改yml配置
server:
port: 7002
eureka:
instance:
hostname: eureka2.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机就是7001自己
#defaultZone: http://localhost:7001/eureka/
#集群指向其它eureka
defaultZone: http://eureka1.com:7001/eureka/
4.新建启动类:
package cn.qz.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * @Author: qlq * @Description * @Date: 9:07 2020/10/3 */ @SpringBootApplication @EnableEurekaServer public class EurekaMain7002 { public static void main(String[] args) { SpringApplication.run(EurekaMain7002.class, args); } }
5.修改原来7001Server的配置
server: port: 7001 eureka: instance: hostname: eureka1.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #单机就是7001自己 #defaultZone: http://localhost:7001/eureka/ #集群指向其它eureka defaultZone: http://eureka2.com:7002/eureka/
6.启动7001、7002服务查看控制台:(可以看到集群信息搭建完成)
2.2 payment支付服务集群
1.新建模块cloud-provider-payment8082
2.修改pom配置
<?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>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-provider-payment8082</artifactId> <dependencies> <!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</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> <!-- spring-boot整合mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-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> <!-- springdata jpa依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</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> </project>
3修改yml
server: port: 8082 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/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 jpa: showSql: true hibernate.ddlAuto: update database-platform: org.hibernate.dialect.MySQL5InnoDBDialect mybatis-plus: mapperLocations: classpath:mapper/*.xml type-aliases-package: cn.qz.cloud.bean # 所有Entity别名类所在包 eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #单机版 #defaultZone: http://localhost.com:7001/eureka # 集群版 defaultZone: http://eureka1.com:7001/eureka,http://eureka2.com:7002/eureka
4.入口类以及业务类同cloud-provider-payment8081
5.两个服务增加一个接口:(getServerPort)
package cn.qz.cloud.controller; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.service.PaymentService; import cn.qz.cloud.utils.JSONResultUtil; import com.baomidou.mybatisplus.service.IService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author: qlq * @Description * @Date: 14:49 2020/9/25 */ @RestController @RequestMapping("/pay") public class PaymentController extends AbstractController<Payment, Long> { @Autowired private PaymentService service; @Value("${server.port}") private String serverPort; @Override public IService<Payment> getBaseService() { return service; } @GetMapping("/getServerPort") public JSONResultUtil<String> getServerPort() { return JSONResultUtil.successWithData(serverPort); } }
2.3. 订单模块修改
1.修改yml
eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #单机版 #defaultZone: http://localhost:7001/eureka # 集群版 defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka
2.增加接口
package cn.qz.cloud.controller; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.utils.JSONResultUtil; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.List; import java.util.Map; /** * @Author: qlq * @Description * @Date: 22:09 2020/9/25 */ @RestController @RequestMapping("/consumer") public class OrderController { private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; @Resource private RestTemplate restTemplate; @PostMapping("/pay/save") public JSONResultUtil<Payment> save(@RequestBody Payment payment) { return restTemplate.postForObject(PAYMENT_URL + "/pay/save", payment, JSONResultUtil.class); } @GetMapping("/pay/listAll") public JSONResultUtil<List<Map<String, Object>>> listAll() { return restTemplate.getForObject(PAYMENT_URL + "/pay/listAll", JSONResultUtil.class); } @GetMapping("/pay/getServerPort") public JSONResultUtil<String> getServerPort() { return restTemplate.getForObject(PAYMENT_URL + "/pay/getServerPort", JSONResultUtil.class); } }
3.启动服务:
依次启动eurekaserver7001、70002、payment8081、8082、订单80
4.访问eureka服务查看:
5.访问订单服务的http://localhost/consumer/getServerPort地址
可以看到是8081、8082端口轮询。
4.Eureka补充
1.actuator完善信息
存在的两个问题:
1. 服务名称显示主机名称,不规范
2.服务的地址信息没有显示
鼠标悬浮的时候没有显示IP地址,我们希望显示IP地址。
解决办法:application.yml修改如下配置
eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #单机版 #defaultZone: http://localhost:7001/eureka # 集群版 defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka instance: instance-id: payment8081 #访问路径可以显示IP地址 prefer-ip-address: true
查看效果如下:
2. 服务发现discovery
DiscoveryClient 可以获取很多的服务信息,包括服务名称以及服务实例信息ServiceInstance。如下:
@RestController @RequestMapping("/pay") @Slf4j public class PaymentController extends AbstractController<Payment, Long> { @Autowired private PaymentService service; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/discovery") public JSONResultUtil<List<String>> discovery() { List<String> services = discoveryClient.getServices(); log.info("services: {}", services); List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); for (ServiceInstance serviceInstance :serviceInstances ) { log.info("InstanceId: {}, Host: {}, Port: {}", serviceInstance.getInstanceId(),serviceInstance.getHost(), serviceInstance.getPort()); } return JSONResultUtil.successWithData(services); } }
查看打印的日志如下:
2020-10-03 22:37:28.968 INFO 7892 --- [nio-8082-exec-1] c.qz.cloud.controller.PaymentController : services: [cloud-payment-service]
2020-10-03 22:37:28.980 INFO 7892 --- [nio-8082-exec-1] c.qz.cloud.controller.PaymentController : InstanceId: payment8081, Host: 192.168.1.36, Port: 8081
2020-10-03 22:37:28.981 INFO 7892 --- [nio-8082-exec-1] c.qz.cloud.controller.PaymentController : InstanceId: payment8082, Host: 192.168.1.36, Port: 8082
补充:Eureka服务发现实际过程
在Eureka环境中,discoverClient实际上是org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient,其注入过程在:org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration#discoveryClient
@Bean @ConditionalOnMissingBean public EurekaDiscoveryClient discoveryClient(EurekaClient client, EurekaClientConfig clientConfig) { return new EurekaDiscoveryClient(client, clientConfig); }
EurekaDiscoveryClientConfiguration 配置类会在引入包的时候自动加载,是在spring-cloud-netflix-eureka-client-2.2.1.RELEASE.jar!\META-INF\spring.factories 配置的
查看org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient#getServices:
@Override public List<String> getServices() { Applications applications = this.eurekaClient.getApplications(); if (applications == null) { return Collections.emptyList(); } List<Application> registered = applications.getRegisteredApplications(); List<String> names = new ArrayList<>(); for (Application app : registered) { if (app.getInstances().isEmpty()) { continue; } names.add(app.getName().toLowerCase()); } return names; }
可以看到是获取缓存在com.netflix.discovery.shared.Applications#applications 的信息。这个信息是通过scheduler任务调度器去定时拿取的。
3.Eureka自我保护机制和禁止自我保护
1.自我保护机制
总结一句话就是:某一时刻某个服务不可用了,Eureka不会立即清掉服务,依旧会对该服务的信息进行保存。
官方解释:自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
2.关闭自我保护
在自我保护模式开启的情况下,当服务挂掉之后,eureka中不会删掉服务,消费者仍然会调用该服务接口造成连接超时异常。
开发环境的话建议关掉自我保护,生产环境开启自我保护。
关闭自我保护之后从Eureka查看,从web查看提示如下:
1. Eureka服务端配置如下:
eureka: instance: hostname: eureka1.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #单机就是7001自己 #defaultZone: http://localhost:7001/eureka/ #集群指向其它eureka defaultZone: http://eureka2.com:7002/eureka/ server: #服务端是否开启自我保护机制 (默认true) enable-self-preservation: false #扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒 eviction-interval-timer-in-ms: 2000
2.Eureka客户端如下:
eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #单机版 #defaultZone: http://localhost:7001/eureka # 集群版 defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka instance: instance-id: payment8081 #访问路径可以显示IP地址 prefer-ip-address: true # 客户端向注册中心发送心跳的时间间隔,(默认30秒) lease-renewal-interval-in-seconds: 1 #Eureka注册中心(服务端)在收到客户端心跳之后,等待下一次心跳的超时时间,如果在这个时间内没有收到下次心跳,则移除该客户端。(默认90秒) lease-expiration-duration-in-seconds: 2
补充: restTemplate 有两种方法,xxxForObject和xxxForEntity
ForObject直接返回结果,ForEntity会返回详细的响应头、状态码等信息。如下:
ResponseEntity<JSONResultUtil> entity = restTemplate.getForEntity(PAYMENT_URL + "/pay/getServerPort", JSONResultUtil.class); HttpStatus status = entity.getStatusCode(); JSONResultUtil<String> result = entity.getBody(); HttpHeaders headers = entity.getHeaders();
补充: eureka可以自定义元数据
例如:
1. application.yml增加元数据配置:
eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #单机版 defaultZone: http://localhost:7001/eureka # 集群版 # defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka instance: instance-id: payment8081 #访问路径可以显示IP地址 prefer-ip-address: true metadata-map: key1: value1 key2: value2
元数据的值也可以用${anotherKey}获取另一个key的值。这时候启动服务,我们可以查看eureka管理界面,如下:
$ curl http://localhost:7001/eureka/apps/CLOUD-PAYMENT-SERVICE % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1642 0 1642 0 0 17655 0 --:--:-- --:--:-- --:--:-- 34936<application> <name>CLOUD-PAYMENT-SERVICE</name> <instance> <instanceId>payment8081</instanceId> <hostName>192.168.1.6</hostName> <app>CLOUD-PAYMENT-SERVICE</app> <ipAddr>192.168.1.6</ipAddr> <status>UP</status> <overriddenstatus>UNKNOWN</overriddenstatus> <port enabled="true">8081</port> <securePort enabled="false">443</securePort> <countryId>1</countryId> <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"> <name>MyOwn</name> </dataCenterInfo> <leaseInfo> <renewalIntervalInSecs>30</renewalIntervalInSecs> <durationInSecs>90</durationInSecs> <registrationTimestamp>1604827931637</registrationTimestamp> <lastRenewalTimestamp>1604828381782</lastRenewalTimestamp> <evictionTimestamp>0</evictionTimestamp> <serviceUpTimestamp>1604827931109</serviceUpTimestamp> </leaseInfo> <metadata> <key1>value1</key1> <key2>value2</key2> <management.port>8081</management.port> <jmx.port>64188</jmx.port> </metadata> <homePageUrl>http://192.168.1.6:8081/</homePageUrl> <statusPageUrl>http://192.168.1.6:8081/actuator/info</statusPageUrl> <healthCheckUrl>http://192.168.1.6:8081/actuator/health</healthCheckUrl> <vipAddress>cloud-payment-service</vipAddress> <secureVipAddress>cloud-payment-service</secureVipAddress> <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer> <lastUpdatedTimestamp>1604827931637</lastUpdatedTimestamp> <lastDirtyTimestamp>1604827931099</lastDirtyTimestamp> <actionType>ADDED</actionType> </instance> </application>
http://ip:port/eureka/apps 可以查看所有服务信息,http://ip:port/eureka/apps/服务名称 可以查看单个服务信息。
2. Controller 用discoveryClient获取服务信息,包括元数据信息
@GetMapping("/serviceInstanceInfos") public JSONResultUtil<List<ServiceInstance>> serviceInstanceInfos() { List<String> services = discoveryClient.getServices(); log.info("services: {}", services); List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); return JSONResultUtil.successWithData(serviceInstances); }
结果:
{ "success": true, "code": "200", "msg": "", "data": [ { "host": "192.168.1.6", "port": 8081, "metadata": { "key1": "value1", "key2": "value2", "management.port": "8081", "jmx.port": "60098" }, "secure": false, "uri": "http://192.168.1.6:8081", "serviceId": "CLOUD-PAYMENT-SERVICE", "instanceId": "payment8081", "instanceInfo": { "instanceId": "payment8081", "app": "CLOUD-PAYMENT-SERVICE", "appGroupName": null, "ipAddr": "192.168.1.6", "sid": "na", "homePageUrl": "http://192.168.1.6:8081/", "statusPageUrl": "http://192.168.1.6:8081/actuator/info", "healthCheckUrl": "http://192.168.1.6:8081/actuator/health", "secureHealthCheckUrl": null, "vipAddress": "cloud-payment-service", "secureVipAddress": "cloud-payment-service", "countryId": 1, "dataCenterInfo": { "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", "name": "MyOwn" }, "hostName": "192.168.1.6", "status": "UP", "overriddenStatus": "UNKNOWN", "leaseInfo": { "renewalIntervalInSecs": 30, "durationInSecs": 90, "registrationTimestamp": 1604828867711, "lastRenewalTimestamp": 1604828867711, "evictionTimestamp": 0, "serviceUpTimestamp": 1604828682411 }, "isCoordinatingDiscoveryServer": false, "metadata": { "key1": "value1", "key2": "value2", "management.port": "8081", "jmx.port": "60098" }, "lastUpdatedTimestamp": 1604828867711, "lastDirtyTimestamp": 1604828867030, "actionType": "ADDED", "asgName": null }, "scheme": null } ] }
补充:application.properties 和 yml文件可以使用${}取其他变量
比如:testVal=${testVal2:this is default} 是取变量testVal2的值,取不到就是冒号后面的默认值;如果testVal2有值就取testVal2的值。
补充:yml和properties文件也可以获取maven中的属性
(1)pom中增加属性
<properties> <key3pom>value3</key3pom> </properties>
build 标签增加如下属性:
<build> <resources> <resource> <directory>src/main/resources</directory> <!--开启过滤,用指定的参数替换directory下的文件中的参数--> <filtering>true</filtering> </resource> </resources> </build>
(2) yml中使用@var@ 获取pom中属性
server: port: 8081 spring: application: name: cloud-payment-service # sleuth链路追踪 # zipkin: # base-url: http://localhost:9411 # sleuth: # sampler: #采样取值介于 0到1之间,1则表示全部收集 # probability: 1 datasource: type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包 url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 jpa: showSql: true hibernate.ddlAuto: update database-platform: org.hibernate.dialect.MySQL5InnoDBDialect mybatis-plus: mapperLocations: classpath:mapper/*.xml type-aliases-package: cn.qz.cloud.bean # 所有Entity别名类所在包 eureka: client: #表示是否将自己注册进EurekaServer默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: #单机版 defaultZone: http://localhost:7001/eureka # 集群版 # defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka instance: instance-id: payment8081 #访问路径可以显示IP地址 prefer-ip-address: true metadata-map: key1: value1 key3: @key3pom@ # 读取pom.xml 声明的属性 key3: @key3pom@
(3)Controller 测试
@Value("${key3}") private String key3; @GetMapping("/keys") public JSONResultUtil<String> keys() { return JSONResultUtil.successWithData(key3); }
测试:
$ curl http://root:8081/pay/keys % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- 100 54 0 54 0 0 1148 0 --:--:-- --:--:-- --:--:-- 3375{"success":true,"code":"200","msg":"","data":"value3"}
(4) 查看元数据
补充:局域网内部可以通过主机名来进行通信
今天发现局域网内部可以通过主机名称来进行通信。局域网内部ping的时候也可以直接ping主机。例如:
$ curl http://root:8081/pay/discovery % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- 100 71 0 71 0 0 1543 0 --:--:-- --:--:-- --:--:-- 4733{"success":true,"code":"200","msg":"","data":["cloud-payment-service"]}
如上,root是计算机名称。并且没有在hosts内配置域名映射。
关于其大致原理可以参考:https://wenku.baidu.com/view/95af2d48a76e58fafab003a8.html
补充:eureka 访问信息并且返回JSON格式的数据:
eureka 服务端是以Jersey 接受客户端请求,默认返回的是XML格式的数据,也可以返回JOSN的数据
查看所有服务信息: curl --header 'Accept: application/json' http://localhost:7001/eureka/apps 查看单个服务的信息: curl --header 'Accept: application/json' http://localhost:7001/eureka/apps/CLOUD-PROVIDER-HYSTRIX-PAYMENT
补充:Eureka 整合SpringSecurity实现登陆时候权限校验
在普通的开发过程中Eureka 可以直接访问到,不需要增加账号、密码;但是有时候为了安全Eureka 需要增加密码机制。Eureka 可以集成SpringSecurity实现账号机制
1. 引入security 相关依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
2. application.yml 配置security 相关配置
spring: security: user: name: eureka password: 123456
3. 增加配置类(这个配置类必须有,否则客户端使用账号密码也注册不上来)
package cn.qz.cloud.configuration; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); super.configure(http); } }
4. 接下来正常访问Eureka 即可,Eureka 会重定向到登录界面 http://localhost:7001/login, 然后正常登录即可
5. Eureka 客户端注册到Eureka 需要验证账号密码
defaultZone: http://eureka:123456@localhost:7001/eureka
6. curl访问的时候增加账号、密码
curl --header 'Accept: application/json' http://eureka:123456@localhost:7001/eureka/apps