编写服务(客户端)
编写班级服务 服务提供方(9010)
步骤一:编写项目(服务),cloud-classes-service-1007
步骤二:修改pom.xml文件,添加 eureka client依赖
<dependencies> <!--web起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--spring boot监控(可选)--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
步骤三:创建yml文件,确定eureka注册中心的位置
server: port: 9010 spring: application: name: classes-service eureka: client: service-url: #注册中心位置 defaultZone: http://localhost:10086/eureka/ instance: #web页面显示效果和访问路径 instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} prefer-ip-address: true
步骤四:编写启动类,添加注解
package com.czxy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ClassesApplication { public static void main(String[] args) { SpringApplication.run(ClassesApplication.class,args); } }
步骤五:启动项目,并测试
步骤六:提供查询所有班级
package com.czxy.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/classes") public class ClassesController { @GetMapping public List<String> findAll(){ List<String> list = new ArrayList<>(); list.add("Java12班"); list.add("Java34班"); return list; } }
编写学生服务 服务调用方(9020)
步骤一:编写项目(服务),cloud-student-service-1007
步骤二:修改pom.xml文件, (和eureka_service一样)
<dependencies> <!--web起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--spring boot监控(可选)--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
步骤三:创建yml文件
server: port: 9020 spring: application: name: student-service eureka: client: service-url: #注册中心位置 defaultZone: http://localhost:10086/eureka/ instance: #web页面显示效果和访问路径 instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} prefer-ip-address: true
步骤四:编写启动类
package com.czxy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class StudentApplication { public static void main(String[] args) { SpringApplication.run(StudentApplication.class,args); } }
步骤五:启动,并测试
功能:学生服务调用班级服务
步骤一:编写HttpConfig,用于配置RestTemplate
package com.czxy.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class HttpConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
步骤二:编写ClassesDao,完成远程调用
package com.czxy.dao; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Repository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.List; @Repository public class ClassesDao { @Resource private RestTemplate restTemplate; @GetMapping public List<String> findAll(){ ResponseEntity<List> entity = restTemplate.getForEntity("http://localhost:8082/classes", List.class); List<String> list = entity.getBody(); return list; } }
步骤三:编写StudentService,调用ClassesDao
package com.czxy.service; import com.czxy.dao.ClassesDao; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class StudentService { @Resource private ClassesDao classesDao; public List<String> findAll() { return classesDao.findAll(); } }
步骤四:编写StudentController,调用StudentService直接显示数据
package com.czxy.controller; import com.czxy.service.StudentService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; @RestController @RequestMapping("/student") public class StudentController { @Resource private StudentService studentService; @GetMapping public List<String> findAll(){ return studentService.findAll(); } }
步骤五:测试
优化:服务调用
修改HttpConfig类,使RestTemplate支持通过服务名调用
修改Dao,使用服务名调用
@GetMapping public List<String> findAll(){ ResponseEntity<List> entity = restTemplate.getForEntity("http://classes-service/classes", List.class); List<String> list = entity.getBody(); return list; }
Eureka高级
优化集群:高可用的Eureka Server
Eureka Server即服务的注册中心,在刚才的案例中,我们只有一个Eureka Server,事实上Eureka Server也可以是一个集群,形成高可用的Eureka中心。
服务同步
多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。
动手搭建高可用的EurekaServer
我们假设要搭建两条EurekaServer的集群,端口分别为:10086和10087
步骤一:分别为两个端口配置yml文件
application-10086.yml 配置
# 端口号 server: port: 10086 # 服务名称 spring: application: name: cloud-eureka-1007 # eureka的配置 eureka: client: service-url: defaultZone: http://localhost:10087/eureka/ # 注册中心的地址 register-with-eureka: true # 是否注册自己的信息到注册中心,默认是true fetch-registry: true # 是否拉取其它服务的信息,默认是true
application-10087.yml 配置
# 端口号 server: port: 10087 # 服务名称 spring: application: name: cloud-eureka-1007 # eureka的配置 eureka: client: service-url: defaultZone: http://localhost:10086/eureka/ # 注册中心的地址 register-with-eureka: true # 是否注册自己的信息到注册中心,默认是true fetch-registry: true # 是否拉取其它服务的信息,默认是true
所谓的高可用注册中心,其实就是把EurekaServer自己也作为一个服务进行注册,这样多个EurekaServer之间就能互相发现对方,从而形成集群。因此我们做了以下修改:
- 删除了register-with-eureka=false和fetch-registry=false两个配置。因为默认值是true,这样就会吧自己注册到注册中心了。
- 把service-url的值改成了另外一台EurekaServer的地址,而不是自己
步骤二:配置启动器
-Dspring.profiles.active=10086
步骤三:测试
注意:10086端口启动后,将一直报错,再等待10087端口,10087启动后错误消失
步骤四:将“classes-service”注册到10086,将自动同步到10087
步骤七:手动将服务注册到Eureka集群
因为EurekaServer不止一个,可以通过service-url设置多个注册地址
eureka: client: service-url: #注册中心位置,多个地址以','隔开 defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka
优化:服务提供者配置
服务提供者要向EurekaServer注册服务,并且完成服务续约等工作。
服务注册
服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-erueka=true参数是否正确,事实上默认就是true。如果值确实为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,完成注册操作。
服务续约
在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew);
有两个重要参数可以修改服务续约的行为:
eureka: client: lease-renewal-interval-in-seconds: 5 #服务续约(renew)的间隔,默认值90秒 lease-expiration-duration-in-seconds: 10 #服务失效时间,默认为30秒
- lease-expiration-duration-in-seconds:服务失效时间,默认值90秒
- lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为30秒
也就是说,默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。/
但是在开发时,这个值有点太长了,经常我们关掉一个服务,会发现Eureka依然认为服务在活着。所以我们在开发阶段可以适当调小。
server: port: 8080 spring: application: name: service eureka: client: service-url: #注册中心位置 defaultZone: http://localhost:10086/eureka/ instance: #web页面显示效果和访问路径 instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} prefer-ip-address: true lease-renewal-interval-in-seconds: 5 #5秒一次心跳 lease-expiration-duration-in-seconds: 10 #10秒及过期
优化:服务消费者配置
获取服务列表
当服务消费者启动是,会检测eureka.client.fetch-registry=true参数的值,如果为true,则会从Eureka Server服务的列表只读备份,然后缓存在本地。并且每隔30秒会重新获取并更新数据。我们可以通过下面的参数来修改:
eureka: client: registry-fetch-interval-seconds: 5
server: port: 9090 spring: application: name: client eureka: client: service-url: #注册中心位置 defaultZone: http://localhost:10087/eureka/ registry-fetch-interval-seconds: 5 #从注册中心,获得列表的间隔时间 instance: #web页面显示效果和访问路径 instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} prefer-ip-address: true
生产环境中,我们不需要修改这个值。
但是为了开发环境下,能够快速得到服务的最新状态,我们可以将其设置小一点。
注册中心优化:失效剔除和自我保护
服务端配置
eureka.server.enable-self-preservation ,是否开启自我保护模式,默认为true。
eureka.server.eviction-interval-timer-in-ms, 清理无效节点的时间间隔,默认60000毫秒
# eureka的配置 eureka: server: enable-self-preservation: true #是否开启自我保护模式 eviction-interval-timer-in-ms: 4000 # 清理无效节点的时间间隔
失效剔除
有些时候,我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server需要将这样的服务剔除出服务列表。因此它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除。
可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒,生成环境不要修改。
这个会对我们开发带来极大的不变,你对服务重启,隔了60秒Eureka才反应过来。开发阶段可以适当调整,比如10S
# eureka的配置 eureka: server: eviction-interval-timer-in-ms: 4000 # 清理无效节点的时间间隔(缺省为60*1000ms)
自我保护
我们关停一个服务,就会在Eureka面板看到一条警告:
这是触发了Eureka的自我保护机制。当一个服务未按时进行心跳续约时,Eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。
但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭自我保护模式:
# eureka的配置 eureka: server: enable-self-preservation: true #是否开启自我保护模式(缺省为true)
优化总结