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

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

  1. 创建一个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 + "!";
  }
}

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

  1. 创建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

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值