随着业务的发展,微服务应用也随之增加,这些服务的管理和治理会越来越难,并且集群规模、服务位置、服务命名都会发生变化,手动维护的方式极易发生错误或是命名冲突等问题。而服务治理正是为了解决这个问题,服务治理是微服务架构中最为核心和基础的模块,它主要实现各个微服务实例的自动化注册和发现。
在服务治理框架中,都会构建一个或多个服务注册中心。 每个服务模块向注册中心登记自己所提供的服务,将主机host、端口号、版本号、通信协议等一些附加信息告知注册中心,注册中心按服务名分类组织服务清单。 服务注册中心还需要以心跳的方式去监测清单中的服务是否可用,若不可用需要从服务清单中剔除,达到排除故障服务的效果。
服务间调用不再通过指定具体实例地址来实现,而是通过向服务名发起请求调用实现。 服务调用方需要先从服务注册中心获取所有服务的实例清单,才能实现对具体服务实例的访问。 服务调用方在发起调用时,会以某种策略取出一个具体的服务实例进行服务调用(客户端负载均衡)。 在生产环境中为了考虑性能等因素,不会采用每次都向服务注册中心获取服务的方式,并且不同的应用场景在缓存和服务剔除等机制上也会采用不同的实现策略。
Spring Cloud Eureka 是基于 Netflix Eureka 来实现服务注册和发现的。它主要包括两个组件:
Eureka 基础架构
基础架构图
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaServerApplication.class, args);
}
}
server.port=9999
#eureka
eureka.instance.hostname=127.0.0.1
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
可以看到 Eureka 的信息面板,其中 Instances currently registered with Eureka 中列表显示 No instances available,说明该注册中心还没有注册任何服务。
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@SpringBootApplication
//@EnableEurekaClient 该注解在采用eureka作为注册中心时使用,场景较为单一
@EnableDiscoveryClient //场景更为广泛
public class SpringCloudEurekaServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaServiceApplication.class, args);
}
}
@EnableEurekaClient
和 @EnableDiscoveryClient
在当前示例中使用效果好是一样的,@EnableEurekaClient
注解在采用eureka作为注册中心时使用,场景较为单一,@EnableDiscoveryClient
场景更为广泛。
server.port=8888
spring.application.name=spring-cloud-eureka-service
#info 应用信息
info.app.name=spring-cloud-eureka-service
info.app.version=v1.0.0
info.app.description=spring-cloud-eureka-service
#eureka
eureka.instance.hostname=127.0.0.1
#每隔5s心跳一次,证明本服务还活着
eureka.instance.lease-renewal-interval-in-seconds=5
#本服务10s内没有心跳,就将该服务从服务端剔除
eureka.instance.lease-expiration-duration-in-seconds=10
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9999/eureka/
在服务注册中心的控制台中我们可以看到如下输出,说明服务被注册成功了。
c.n.e.registry.AbstractInstanceRegistry : Registered instance SPRING-CLOUD-EUREKA-SERVICE/192.168.101.201:spring-cloud-eureka-service:8888 with status UP (replication=false)
而在 Eureka 的信息面板上,在 Instances currently registered with Eureka 列表中同样可以看到服务的注册信息。如下图:
上面介绍了单节点模式的服务注册中心,不过在实际生产环境中,通常不会采用这种模式。在分布式系统中,服务注册中心是非常重要的组成部分,如果是单节点模式,发生故障的话将会是毁灭性的灾害。所以为了维护服务的高可用性,通常采用集群的解决方案。
Eureka 的服务治理设计中,所有的节点既是服务提供方,也是服务消费方,服务注册中心也不例外。Eureka 通过互相注册服务的方式,以实现服务清单的互相同步,达到高可用的效果。
server.port=9991
spring.application.name=eureka-server
spring.profiles.active=nodea
#eureka
eureka.instance.hostname=nodea
#设置微服务调用地址为IP优先(缺省为false)
#eureka.instance.prefer-ip-address=true
eureka.client.serviceUrl.defaultZone=http://nodeb:9992/eureka/
server.port=9992
spring.application.name=eureka-server
spring.profiles.active=nodeb
#eureka
eureka.instance.hostname=nodeb
#设置微服务调用地址为IP优先(缺省为false)
#eureka.instance.prefer-ip-address=true
eureka.client.serviceUrl.defaultZone=http://nodea:9991/eureka/
127.0.0.1 nodea
127.0.0.1 nodeb
http://nodea:9991/
和http://nodeb:9992/
,我们可以看到两个节点都已经被注册,如下图所示:eureka.client.serviceUrl.defaultZone=http://nodea:9991/eureka/,http://nodeb:9992/eureka/
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
spring.application.name=spring-cloud-ribbon-consumer
server.port=8081
eureka.client.serviceUrl.defaultZone=http://nodea:9991/eureka/,http://nodeb:9992/eureka/
通过 @EnableDiscoveryClient
注解将应用注册为 Eureka 客户端,获得服务发现能力。
创建 RestTemplate 的 Spring Bean 实例用来调用服务。
通过 @LoadBalanced
注解来开启客户端的负载均衡。
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudRibbonConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(SpringCloudRibbonConsumerApplication.class, args);
}
}
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/test")
public String test() {
return restTemplate.getForEntity("http://spring-cloud-eureka-service/test", String.class).getBody();
}
}
spring-cloud-eureka-service 为服务注册中心的应用名称,大小写均可。
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
spring.application.name=spring-cloud-feign-consumer
server.port=8080
eureka.client.serviceUrl.defaultZone=http://nodea:9991/eureka/,http://nodeb:9992/eureka/
通过 @EnableDiscoveryClient
注解将应用注册为 Eureka 客户端,获得服务发现能力。
通过 @EnableFeignClients
注解来启用feign进行远程调用。
@SpringBootApplication
@EnableDiscoveryClient//启用服务注册与发现
@EnableFeignClients//启用feign进行远程调用
public class SpringCloudFeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFeignConsumerApplication.class, args);
}
}
@FeignClient(name = "spring-cloud-eureka-service")
public interface TestService {
@RequestMapping("/test")
public String test();
}
spring-cloud-eureka-service 为服务注册中心的应用名称,大小写均可。
此接口中的方法和远程服务中contoller中的方法名和参数需保持一致。
@RestController
public class ConsumerController {
@Autowired
private TestService testService;
@RequestMapping("/test")
public String test() {
return testService.test();
}
}
阅读原文,底部获取示例代码。
如果文章对您有帮助,请扫码关注下我的公众号,文章持续更新中...