Spring cloud中的服务治理,大致分为两部分:服务注册,服务发现。
服务治理是微服务架构中最核心最基础的模块,主要用来实现各个微服务模块之间的自动化注册与发现。在传统的架构中,随着服务或者模块原来越多,需要大量的静态配置来维护实例清单。这种状况,无论对于开发还是运维,都是比较头疼的事。为了解决微服务架构中服务实例维护问题,产生了大量的服务治理框架和产品,这些框架和产品都是围绕着服务注册与服务发现这两大机制完成对微服务实例的自动化管理。
1.基本概念
服务注册:服务治理框架中,都会有一个注册中心,每个模块或者服务都得向注册中心注册自己,登记自己提供的服务,将主机与端口号,版本号,通信协议等附加信息告知注册中心,注册中心按服务名分类组织服务清单。通常,同样的一个服务可能会有多个进程,比如服务A,有两个进程分别是:192.168.0.100:8000,192.168.0.101:8000。一个服务多个实例最显而易见的好处是:当一个实例由于种种原因挂了(比如网络状况,比如IO异常导致的),那么注册中心(Eureka Server)就能快速切换到另一个实例提供服务,这样保证了整个系统的稳定和可靠。实现服务提供者的高可用。
而要实现这一点,这两个实例都需要向注册中心注册自己,并提供相同的服务名。
服务发现:因为各个服务都在服务治理框架下运行,服务间的调用不再通过指定具体的实例地址来访问,比如使用http client这种方式。因为在服务注册中,每个服务都提供了一个独一无二的服务名。因此服务间的调用就是通过服务名实现的。所以,服务调用方在调用服务提供方的接口时,向注册中心获取所有服务实例清单,找到服务提供方的服务名,再根据服务名取出服务实例,比如前面提到的192.168.0.100:8000,192.168.0.101:8000。而具体调用哪一个实例,需要以某种策略来进行决定,这就是客户端负载均衡,这里只是粗略地讲个概念。
2. 服务注册
Spring cloud的服务注册使用的组件是 Spring cloud Eureka,包括服务端组件和客户端组件。
Eureka服务端,也称之为服务注册中心,并且支持高可用配置。Eureka客户端,需要向Eureka注册自己并对外提供服务的发现或者消费。客户端通过注解和参数配置的方式向注册中心注册自己,程序在运行时,客户端周期性向服务端发送心跳来更新它的服务租约,同时从Eureka服务端获取服务清单并缓存到本地,周期性刷新服务状态。
搭建服务注册中心:
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.liang.springcloud</groupId>
<artifactId>spring-cloud-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
需要提醒的是,spring boot和spring cloud版本需要对应。
入口方法:
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudTestApplication.class, args);
}
}
注意,@EnableEurekaServer注解启动一个服务注册中心。
配置文件 application.properties:
server.port=1111
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.client.register-with-eureka=false: 由于该应用为注册中心,所以设置为false,表示不向自己注册自己。
eureka.client.fetch-registry=false: 因为注册中心职责就是维护服务实例,设为false,表示不需要去检索服务。
eureka.client.serviceUrl.defaultZone: 注册地址,在本例中,其实就是 http://localhost:1111/eureka/
启动程序,访问http://localhost:1111/eureka/,可以看到界面如下:
下面是一个另一个sprig boot服务,向注册中心注册自己。
注册服务提供者:
pom.xml文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.liang</groupId>
<artifactId>service_produ</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service_produ</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
入口方法:
/**
* 服务提供者
*/
@EnableDiscoveryClient
@SpringBootApplication
public class ServiceProduApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProduApplication.class, args);
}
}
配置文件application.yml:
spring:
application:
name: hello-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
其中,spring.application.name是服务名,必须有。eureka.client.serviceUrl.defaultZone是注册中心的注册地址,必须和注册中心保持一致。必须有。
提供接口的controller:
@RestController
public class HelloController {
private final Logger logger=Logger.getLogger("liang");
//该对象可以打印服务相关的内容
@Autowired
private DiscoveryClient client;
@RequestMapping("/hello")
public String index(){
ServiceInstance instance=client.getLocalServiceInstance();
//打印主机名和服务名
logger.info("helloHost:"+instance.getHost()+",service_id:"+instance.getServiceId());
return "Hello world";
}
}
先后启动服务注册中心和这里的hello-service服务,在Eureka的信息面板上可以看到hello-service服务。访问"/hello"接口,日志会输出主机名和服务名。
高可用注册中心:
在分布式环境中,需要充分考虑一个节点发生故障对系统造成的影响。所以各个组件尽量以高可用的模式进行部署。注册中心也一样,如果系统中只有一个注册中心的实例,一旦发生故障,那么整个系统就瘫痪了,这不是我们想看到的。
其实,服务注册中心既可以是服务提供方,也可以是服务消费方(需要修改注册中心的配置文件)。Eureka Server的高可用其实就是将自己作为服务向其他的注册中心注册自己,这样可以实现服务实例清单的同步,达到高可用的效果。在Eureka 客户端(服务提供方),需要将这些注册中的注册地址都写到配置文件中,这样一旦某个注册中心出现问题,另外一个注册中心就会立即工作,整个系统不会受影响。
下面是一个spring boot服务,使用Ribbon访问其他服务的接口
3.服务发现与消费
服务消费的组件是Ribbon,Ribbon是一个基于http和tcp的客户端负载均衡器,它可以在客户端配置ribbonServiceList服务端列表去轮询访问以达到负载均衡的作用。
pom.xml:
<groupId>com.liang</groupId>
<artifactId>ribbon-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ribbon-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
入口方法:
/**
* 服务消费者
*/
@EnableDiscoveryClient//该注解让该应用成为Eureka客户端应用
@SpringBootApplication
public class RibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
@Bean
@LoadBalanced//该注解开启客户端负载均衡
RestTemplate restTemplate(){
return new RestTemplate();
}
}
配置文件application.yml:
spring:
application:
name: ribbon-consumer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
server:
port: 9000
无论是服务提供者还是消费者,对于Eureka来说,都是客户端,所以都要有服务名和注册地址。
controller接口调用hello-service中的接口:
@RestController
public class ConsumerController {
@Resource
private RestTemplate restTemplate;
@RequestMapping(value = "ribbon-consumer",method = RequestMethod.GET)
public String helloConsumer(){
//HELLO-SERVICE
return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
}
}
启动服务,调用该接口,就会执行hello-service中的“/hello”接口。