当项目数量很多的时候,一个项目的更新往往需要被动的更新好几个项目。服务与服务之间过于耦合,因此微服务架构诞生了。

在微服务架构中,服务与服务之间不再相互调用,而是统一注册到注册中心中,通过注册中心统一调度。

因此当 A 服务要调用 B 服务,不在是直接 A 找 B,而是 A 到注册中心找 B 注册的地址。通过注册中心来获取服务你不需要关注你调用的项目的 IP 地址,服务器组成等对方的情况,而是直接去注册中心获取即可。


Eureka 和 zookeeper 都是用于提供服务注册和发现的组件。如果要谈一下他们的区别那么得先了解一下 CAP 原则。

CAP 原则指的是在一个分布式系统中,Consistency(一致性)Availability(可用性)Partition tolerance(分区容错性),三者不可兼得。CAP 原则也是 NoSQL 数据库的基石。

一致性(C):在分布式系统中的所有数据备份,在同一时刻需要是一样的值。

可用性(A):在集群中一部分节点故障后,集群整体要能继续响应客户端的读写请求。

分区容忍性(P):一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。


Eureka 是遵循 AP 的,zookeeper 遵循 CP,我们公司用的是 zookeeper,我猜想是因为公司做的是金融业务对于金额账目的一致性有很高的要求,即使服务挂了,也绝不允许客户金额在服务出故障时不断变动,对一致性有很高的要求,因此公司架构师选用的是 zookeeper。

Eureka 案例实践:

首先进官网创建一个 springboot 项目作为主工程,删除主工程的 src 文件夹,然后在主工程的基础上创建三个module:注册中心 eureka ,服务提供者 producer,服务消费者 consumer。

eureka module:


1. pom.xml 中添加依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

2. 启动类中添加 @EnableEurekaServer 注解,该注解的作用是标识该项目为一个注册中心:
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudEurekaApplication.class, args);
    }
}

3. 仅有 @EnableEurekaServer 注解还不够,还需要写入配置文件:
spring.application.name=spring-cloud-eureka
server.port=8000

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/

因为注册中心也会将自己当做客户端尝试注册自己,所以我们可以用下面两条配置来禁用他们eureka.client.register-with-eureka=false,eureka.client.fetch-registry=false。
eureka.client.serviceUrl.defaultZone 用来标识注册中心的默认地址。
启动工程后,访问:http://localhost:8000/,可以看到注册中心页面,其中还没有发现任何服务。

producer module:


1. 同样地,按照注册中心的创建方式,创建一个 module,并且在 pom.xml 添加如注册中心一样的内容。
2. 启动类中添加 @EnableEurekaClient 注解,该注解的作用是标识该项目为一个服务提供者。@EnableDiscoveryClient 注解也可以标识该项目为一个服务提供者。

@EnableDiscoveryClient 基于 spring-cloud-commons,@EnableEurekaClient基于 spring-cloud-netflix。其实用更简单的话来说,就是如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么就需要使用@EnableDiscoveryClient 了。

@EnableEurekaClient
@SpringBootApplication
public class SpringCloudProducerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudProducerApplication.class, args);
    }
}

3. 配置文件 application.properties:
spring.application.name=spring-cloud-producer
server.port=9000
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
  • spring.application.name 是该服务的名字,也是该服务注册到注册中心的名字。
  • eureka.client.serviceUrl.defaultZone 指定该服务注册到注册中心的地址。

启动该工程,重新访问 http://localhost:8000,可以看到注册中心页面中 spring-cloud-producer 服务已经被注册到注册中心了。


4. 创建一个 controller 类,提供一个 hello 服务:
@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String index(@RequestParam("name") String name){
        return "hello "+name+",this is first message";
    }
}

访问 http://127.0.0.1:9000/hello?name=neo 直接可以得到返回值。

consumer module:


1. pom.xml 中添加依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>

2. 配置文件 application.properties:
spring.application.name=spring-cloud-consumer
server.port=9001
feign.hystrix.enabled=true
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

3. 启动类添加 `@EnableDiscoveryClient` 和 `@EnableFeignClients` 注解, `@EnableDiscoveryClient` 用于启用服务注册和发现,将服务注册到注册中心,供其他服务调用。而 `@EnableFeignClients` 用于启用 feign 进行远程调用:
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class SpringCloudConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudConsumerApplication.class, args);
    }
}

4. 一个大型的系统往往是由多个微服务模块组成,各模块之间不可避免需要进行通信,一般我们可以通过内部接口调用的形式,服务 A 提供一个接口,服务 B 通过 HTTP 请求调用服务 A 的接口,为了简化开发,Spring Cloud 提供了一个基础组件方便不同服务之间的 HTTP 调用,那就是 Feign。

Feign 是一个声明式的 HTTP 客户端,它简化了 HTTP 客户端的开发。使用 Feign,只需要创建一个接口并注解,就能很轻松的调用各服务提供的 HTTP 接口。Feign 默认集成了 Ribbon,默认实现了负载均衡


创建一个接口 HelloRemote,通过 Feign 去调用之前我们写的 HTTP 接口:

@FeignClient(name = "spring-cloud-producer")
public interface HelloRemote {
    @RequestMapping(value = "/hello")
    public String hello(@RequestParam("name") String name);
}

name 是要调用的服务地址,表明要去这个服务调用 HTTP 接口。
5. 测试一下能不能成功调用:

Test 测试:

public class Test{
    @Autowired
    HelloRemote helloRemote;
    
    @Test
    public void test(){
        try {
            System.out.println(helloRemote..hello(neo));
        }catch (Exception e){
            e.printStackTrace();
        }
	}
}

或者使用web层调用远程服务看一下:
@RestController
public class ConsumerController {

    @Autowired
    HelloRemote helloRemote;
	
    @RequestMapping("/hello/{name}")
    public String index(@PathVariable("name") String name) {
        return helloRemote.hello(name);
    }
    
} 

依次启动注册中心 eureka ,服务提供者 producer,服务消费者 consumer。

直接看服务提供者 producer,输入 http://127.0.0.1:9000/hello?name=neo 直接可以得到返回值:hello neo,this is first message。

再看服务消费者 consumer,输入 http://localhost:9001/hello/neo 得到同样的结果,说明 feign 成功调用了远程服务 hello,并且返回了结果。

Zookeper 案例实践:

未完待续

参考链接:

CAP 定理的含义

@EnableDiscoveryClient与@EnableEurekaClient区别

springcloud(三):服务提供与调用