在之前的父工程下创建一个maven工程,成为注册中心 添加如下依赖:
<dependencies> <!--eureka-server服务端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!--热部署--> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> <version>1.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>在resources中的application.yml中配置Eureka
server: port: 7001 eureka: instance: hostname: localhost #eureka服务端的实例名称 client: register-with-eureka: false # 表示不向注册中心注册自己 fetch-registry: false # 表示自己就是注册中心,职责就是维护服务实例,不需要去检索服务 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置Eureka Server交互的地址查询和注册服务都要依赖这个地址只需要编写一个启动类即可
package springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer //注册中心 public class EurekaServer7001_App { public static void main(String[] args) { SpringApplication.run(EurekaServer7001_App.class, args); } }运行,来到7001端口 我们发现注册Eureka的地址名称是unknown,是因为我们没有设置访问Eureka的Application Name,就是微服务的名称,所以我们这里来到配置文件中手动进行修改:
spring: application: name: miscroservicecloud-dept eureka: client: # 客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka重新运行,发现我们的微服务名称不再是unknown了,而是我们自己写的名称。
消费者就是要在Eureka上注册自己的相关信息,因此要对消费者做一些配置上的修改,让其能够成功注册。 首先添加如下依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>和之前一样,配置了Eureka就要开启这项集群服务来到启动类,进行启动Eureka ,加上一个注解@EnableDiscoveryClient即可,之后和以上相同,如果我们这个时候重新运行了miscroservicecloudproviderdept8001是无法被检测到注册了的,所以还需要稍微修改一下配置
server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径 type-aliases-package: springcloud.entities # 所有Entity别名类所在包 mapper-locations: # mapper映射文件 - classpath:mybatis/mapper/**/*.xml spring: application: name: miscroservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包 url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称 username: root password: 123456 dbcp2: min-idle: 5 # 数据库连接池的最小维持连接数 initial-size: 5 # 初始化连接数 max-total: 5 # 最大连接数 max-wait-millis: 200 # 等待连接获取的最大超时时间 eureka: client: # 客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka重新运行之后,显示成功。
首先添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>在application.yml中进行相关配置
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://localhost:7001/eureka使用Eureka的目的就是动态获取到地址,所以要对url的获取方式进行修改
package springcloud.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import springcloud.entities.Dept; import java.util.List; @RestController public class DeptController_Consumer { //misroservicecloud-dept // private static final String REST_URL_PREFIX = "http://localhost:8081"; private static final String REST_URL_PREFIX = "http://MISROSERVICECLOUD-DEPT"; /** * 使用RestTemplate访问restful接口 * (url,requestMap,ResponseBean.class) * REST请求地址、请求参数、HTTP响应转换被转换成的对象类型 */ @Autowired RestTemplate restTemplate; @RequestMapping(value = "/consumer/dept/add") public boolean add(Dept dept){ return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add", dept, boolean.class); } @RequestMapping(value = "/consumer/dept/get/{id}") public Dept get(@PathVariable("id")Long id){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get"+id, Dept.class); } @SuppressWarnings("unchecked") @RequestMapping(value = "/consumer/dept/list") public List<Dept> list(){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class); } //测试@EnableDiscoveryClient,消费端可以调用服务发现 @RequestMapping(value = "/consumer/dept/discovery") public Object discovery(){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/discovery", Object.class); } }Eureka中三大核心角色: Eureka Server: Eureka的服务端应用,提供服务注册和发现功能,就是刚刚我们建立的Eureka-Server Service Provider: 提供服务的应用,可以是SpringBoot应用,也可以是其它任意技术实现,只要对外提供的是Rest风格的服务即可。本例中就是我们实现的miscroservicecloudproviderdept8001 Service Consumer: 消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。就是在本例中实现的miscroservicecloudconsumerdept80
Eureka Server:服务注册中心,刚才我们只创建了一个Eureka Server,事实上Eureka Server也可以是一个集群,从而形成高可用的注册中心。 服务同步: 多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。
将defaultZone的地址改变成为其他相邻的地址,做到相互连通。 然后将EurekaServer进行复制,让复制出来的也指向Eureka这项服务 创建第二个注册中心,修改相关的配置
server: port: 7002 eureka: instance: # hostname: localhost #eureka服务端的实例名称 hostname: eureka7002.com client: register-with-eureka: false # 表示不向注册中心注册自己 fetch-registry: false # 表示自己就是注册中心,职责就是维护服务实例,不需要去检索服务 service-url: # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置Eureka Server交互的地址查询和注册服务都要依赖这个地址 defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/同理创建第三个注册中心,记得修改defaultZone让其指向其相邻的地址。 由于我们对于Eureka进行了高可用的注册,所以也必须要对miscroservicecloudproviderdept8001和miscroservicecloudconsumerdept80进行两个Eureka的注册,而不只是7001端口的了,只需要在yml文件中给defaultZone加上域名即可 加了此注册信息主要就是防止其中一个节点挂了,另外的节点还可以继续工作。
运行之后,发现我们现在的微服务是有三个Eureka Server。
平时启动Eureka的时候还是先会报错,然后才能正常启动,现在我们的Eureka一启动首先就会注册自己,但是并不是所有情况下一开始就会注册自己,然后先报错的,这取决于一个设置register-with-eureka: false只要设置为false就不注册了,默认都是注册的。 平时注册的时候,都是以Map形式注册的,Key就是微服务的名字,Value就是InstanceID。
心跳续约的默认时间是30s,每隔30s进行一次心跳的续约,但是时间也是可以配置的,超出时间范围就会被默认为宕机了。 默认配置是这样的:
eureka: instance: lease-expiration-duration-in-seconds: 90 lease-renewal-interval-in-seconds: 30每隔三十秒发送一次心跳,一共90秒也就是三次,如果都没有发送心跳相应,就是宕机了。 但是也不能心跳发送的太快,Eureka也会宕机的,设置默认就好,一般情况下是不会更改这个值的。
服务的提供方会注册自己,而消费方则会拉取服务列表,拉取也是有一定周期的:
eureka: client: registry-fetch-interval-seconds: 30一般情况下,当我们关闭某一项服务时,会触发一个服务下线的REST的请求给EurekaServer,告诉服务中心下线的请求,然后服务中心接收到请求之后就会下线。
失效剔除: 有时我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server需要将这样的服务剔除出服务列表。因此它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除。 eureka: server: eviction-interval-timer-in-ms: 30000 自我保护: 触发了Eureka的自我保护机制。当一个服务未按时进行心跳续约时,Eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。 我们可以手动关闭掉自我保护: eureka: server: enable-self-preservation: false # 关闭自我保护模式(缺省为打开) eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为60*1000ms)Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
首先导入maven的相关依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>修改miscroservicecloudconsumerdept80的cfgbeans包里面的配置类
package springcloud.cfgbeans; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; 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; @Configuration public class ConfigBean { @Bean @LoadBalanced //spring cloud ribbon基于netfix实现的一套客户端 负载均衡的工具 public RestTemplate getRestTemplate(){ return new RestTemplate(); } }总结:Ribbon默认是使用轮询来达到负载均衡,但是我们也可以去手动修改负载均衡机制,比如说随机 只需要在配置类里面写上
@Bean public IRule myRule(){ return new RandomRule(); //目的:用重新选择的随机算法替代默认的轮询 }即可。