首页 > 编程语言 > SpringCloud 服务注册和消费实现过程
2020
10-08

SpringCloud 服务注册和消费实现过程

系统架构

在没有微服务之前有已经有跨服务调用了,比如ServiceB去调用ServiceA中的服务 , 传统模式可以直接在ServiceB中写ServiceA中的服务但是这样是写死了的,不够灵活。

下图就是传统的调用


微服务下的跨系统调用应该是这样的:


此时服务的调用应该是分两个步骤的:
ServiceB 去服务中心拿到ServiceA的地址,如果ServiceA是单机部署,那么这个地址就只有一个,如果ServiceA是集群是集群环境部署,那么发现的地址就是多个。

拿到了ServiceA的地址后,ServiceB再去调用ServiceA的相关服务了。

这样做其实是有很多好处的,首先互相调用的地址可以不用写死,需要的时候直接去服务中心获取,并且服务之间也可以很方便的部署、集群等。

服务注册与消费搭建

1.首先我们创建一个ServiceRegister的普通maven项目,然后在创建一个Eureka的SpringBoot项目作为子项目


下面是Eureka项目的pom.xml 配置

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </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>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

下面是application.yml 配置文件

# 别名
spring:
 application:
  name: eureka
#端口
server:
 port: 1111

# eureka config
eureka:
 client:
  register-with-eureka: false
  fetch-registry: false

注意 这里配置好了后需要在Eureka这个子项目的启动类上加入下面这个注解

@EnableEurekaServer

这个注解的意义是代表这个项目成为一个注册中心

2.创建一个Provider

创建一个叫Provider的SpringBoot项目作为子项目,pom.xml配置

<properties>
  <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.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</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>

下面是application.yml 配置文件

# 别名
spring:
 application:
  name: provider
#端口
server:
 port: 4001
# provider config
eureka:
 client:
  service-url:
   defaultZone: http://localhost:1111/eureka 

spring.application.name 是服务的名称,你可以理解成是别名,其他的服务调用的时候需要使用这个name来调用

server.port 是端口号

eureka.client.service-url.defaultZone 是这个服务需要注册到服务中心地址,这里需要注意的是,如果服务中心是一个集群,这里也可以只写服务中心的一个节点,eureka会自动集群对服务进行同步。

在微服务中,你的项目的pom.xml 中如果存在 spring-cloud-starter-netflix-eureka-client的依赖,并且提供了eureka注册中心的地址那么会默认注册到 Eureka Server 中。

然后我们在 provider 中提供一个简单的服务

@RestController
public class SayHelloController {
 @GetMapping("/sayHello")
 public String SayHello(String name) {
  return "sayHello" + name + "!";
 }
}

这样我们就创建好我们的服务提供者,并且提供了一个简单的服务接口了。

3.创建consumer

创建好了服务提供者,那么我们就来创建服务消费者consumer,创建一个SpringBoot的子项目工程 pom依赖如下

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <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>
    </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>

这个配置和provider 配置几乎是一样的,下面是consumer的application.yml 配置文件

# 别名
spring:
 application:
  name: consumer
#端口
server:
 port: 4002
# provider config
eureka:
 client:
  service-url:
   defaultZone: http://localhost:1111/eureka 

唯一变了就是服务的名称。

配置好了后,我们在consumer的启动类中添加一个RestTemplate的实例,RestTemplate是Spring提供的一个Http请求工具,下面是这个RestTemplate实例的代码

@SpringBootApplication
public class ConsumerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ConsumerApplication.class, args);
  }

  @Bean
  RestTemplate restTemplate() {
    return new RestTemplate();
  }
}

然后我们在consumer 中添加一个UserSayHelloController,在这里去调用provider 提供的服务。

@RestController
public class SayUserHelloController {
  @Autowired
  DiscoveryClient discoveryClient;
  @Autowired
  RestTemplate restTemplate;
  @GetMapping("/sayHello")
  public String hello(String name) {
    List<ServiceInstance> list = discoveryClient.getInstances("provider");
    ServiceInstance instance = list.get(0);
    String host = instance.getHost();
    int port = instance.getPort();
    String s = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);
    return s;
  }
}

上面代码的意思是解释如下:

首先我们在这个Controller中注入了一个DiscoveryClient ,DiscoveryClient可以从Eureka或者从Consul上根据服务名查询一个服务的详细信息,注意DiscoveryClient是下面这个包中的

org.springframework.cloud.client.discovery.DiscoveryClient
@Autowired
 RestTemplate restTemplate;

RestTemplate 就是Spring 给我们提供用来发送Http请求的,这个大多数人应该都是知道的。

List<ServiceInstance> list = discoveryClient.getInstances("provider");

discoveryClient.getInstances 就是调用服务的名称,为什么用List去接受? 那是因为有可能provider 是单机部署 也有可能是集群部署,如果是集群部署的话,那么provider的实例就有多个

ServiceInstance 保存了provider 中 详细的信息、如主机地址、端口号、实例id等。ServiceInstance是一个接口,它有很多给实现类,我们本次的这个项目使用的是EurekaServiceInstance。

ServiceInstance instance = list.get(0);

因为我们只有一个provider 实例,所以我们就用list.get(0) 来获取实例了

获取主机地址

String host = instance.getHost();

获取端口号

int port = instance.getPort();

RestTemplate 的 getForObject 方法接收三个参数。第一个参数是请求地址,请求地址中的 {1} 表示一个参数占位符,第一个参数 String.class 表示返回的参数类型,第三个参数则是一个第一个占位符的具体值。

String s = restTemplate.getForObject("http://" + host + ":" + port + "/hello?name={1}", String.class, name);

最后我们返回这个s 就完成了consumer的编写

下面我们依次的启动Eureka(注册中心)和 服务提供者(provider) 以及 消费者(consumer)

然后在浏览器上输入:
localhost:4002/sayHello?name=jishu
结果如下图所示:

这样我们就顺利的从consumer 去调用了provider 的服务了

DiscoveryClient是哪里来的

归根结底 DiscoveryClient作用就是可以从Eureka 或者Consul 中查询服务的实例,不过DiscoveryClient就是一个接口而已,但是还是有一个实现类, 这个具体的实现类就是CompositeDiscovery,当我们的微服务启动的时候,就会在CompositeDiscoveryClientAutoConfiguration类中配置一个CompositeDiscovery的实例,下面这个就是大名鼎鼎的CompositeDiscoveryClientAutoConfiguration的源码

//这里就是DiscoveryClient的源码了
@Configuration
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)
public class CompositeDiscoveryClientAutoConfiguration {

 //这里返回的是一个实例
	@Bean
	@Primary
	public CompositeDiscoveryClient compositeDiscoveryClient(
			List<DiscoveryClient> discoveryClients) {
		return new CompositeDiscoveryClient(discoveryClients);
	}

}

其实真正调用的是CompositeDiscoveryClient类中的discoveryClients 属性提供的 DiscoveryClient,而discoveryClients 属性默认集合中只有一条数据,就是EurekaDiscoveryClient,最终在EurekaDiscoveryClient类中,通过EurekaClient来获取一个微服务的实例信息了

总结

在不用微服务调用,服务之间的调用是相当繁琐的,并且地址是写死了,那么部署的话是非常的不方便,但是我们提供了注册中心的话,那么我们让服务注册到我们的注册中心中,然后在从注册中心去获取我们的服务信息,这样就有大大的好处,降低了调用的难度。

项目地址

github

到此这篇关于SpringCloud 服务注册和消费实现过程的文章就介绍到这了,更多相关SpringCloud 服务注册 消费内容请搜索自学编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持自学编程网!

编程技巧