前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringCloud详细教程 | 第一篇: 服务的注册与发现Eureka(Greenwich版本)

SpringCloud详细教程 | 第一篇: 服务的注册与发现Eureka(Greenwich版本)

作者头像
小东啊
发布2019-06-26 15:26:27
9240
发布2019-06-26 15:26:27
举报
文章被收录于专栏:李浩东的博客李浩东的博客

微服务是指开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通讯机制,简单的说就是对系统进行拆分,拆分多个服务,每个服务运行在其独立的进程中,服务和服务之间采用轻量级的通信机制相互沟通(通常是基于HTTP的Restful API).每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境、类生产环境等

一. 现在我们需要模拟一个服务调用的场景, 方便后面学习微服务架构

开发环境:JDK8 Spring Boot版本2.1.3 首先我们创建两个Spring Boot项目provider-server 服务提供者

代码语言:javascript
复制
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.1.3.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.li</groupId>    <artifactId>provider-server</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>provider-server</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>    </dependencies>
    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build>
</project>

在程序入口定义一个hello接口 方便调用

代码语言:javascript
复制
package com.li.providerserver;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
/** * 服务提供者 */@RestController@SpringBootApplicationpublic class ProviderServerApplication {
    public static void main(String[] args) {        SpringApplication.run(ProviderServerApplication.class, args);    }
    /**     * 提供hello接口     * @param name     * @return     */    @RequestMapping("/hello")    public String hello(String name){        System.out.println("被调用");        return "hello,"+name;    }}

启动项目,访问接口: http://localhost:8080/hello?name=lhd

consumer-server 服务消费者也就是调用者pom.xml文件

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.1.3.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.li</groupId>    <artifactId>consumer-server</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>consumer-server</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>    </dependencies>
    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build>
</project>

配置文件中修改端口号 8081

在程序入口定义一个hello接口 我们通过RestTemplate远程调用

代码语言:javascript
复制
package com.li.consumerserver;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;
/** * 服务消费者也就是调用者 */@RestController@SpringBootApplicationpublic class ConsumerServerApplication {
    public static void main(String[] args) {        SpringApplication.run(ConsumerServerApplication.class, args);    }
    /**     * 注入bean 才可以使用     * @return     */    @Bean    public RestTemplate restTemplate(){        return new RestTemplate();    }
    /**     * 使用注解@Autowired进行注入即可     */    @Autowired    private RestTemplate restTemplate;    /**     *     * @param name     * @return     */    @RequestMapping("/hello")    public String hello(String name){        // 直接调用provider-server服务的hello接口 ip 端口号 参数等        String hello = restTemplate.getForObject("http://localhost:8080/hello?name=" + name, String.class);        return "调用端口号8080服务成功,返回数据:"+hello;    }}

启动项目, 访问接口: http://localhost:8081/hello?name=lhd

一个简单的远程服务调用案例就实现了

那么问题来了

在consumer中,我们把url地址硬编码到了代码中,不方便后期维护

  • consumer需要记忆provider的地址,如果出现变更,可能得不到通知,地址将失效
  • consumer不清楚provider的状态,服务宕机也不知道
  • provider只有1台服务,不具备高可用性
  • 即便provider形成集群,consumer还需自己实现负载均衡 其实上面说的问题,概括一下就是分布式服务必然要面临的问题:

服务管理

  • 如何自动注册和发现?
  • 如何实现状态监管?
  • 如何实现动态路由?

服务如何实现负载均衡? 服务如何解决容灾问题? 服务如何实现统一配置? 以上的问题,我们都将在SpringCloud中得到答案

二. Eureka注册中心

在刚才的案例中,provider-server对外提供服务,需要对外暴露自己的地址。而consumer(调用者)需要记录服务提供者的地址。将来ip地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的

举个生活中的例子:

在没有滴滴打车之前, 我们出门都是坐出粗车, 有些私家车司机也想去拉客人去挣钱,因为没有网约车这种概念,被称为黑车,而很多人想要约车,但是无奈出租车太少,不方便。私家车很多却不敢拦,而且满大街的车,谁知道哪个才是愿意载人的。一个想要,一个愿意给,就是缺少中间人啊,就好比中介 自从滴滴打车这样的网约车平台出现了,所有想载客的私家车全部到滴滴注册,记录你的车型(服务类型),身份信息(联系方式)。这样提供服务的私家车,在滴滴那里都能找到,一目了然。 此时要叫车的人,只需要打开APP,输入你的目的地,选择车型(服务类型),滴滴自动安排一个符合需求的车到你面前,为你服务

那么问题来了,和我们说的Eureka有什么关系?

Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。 同时,服务提供方与Eureka之间通过“心跳”机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。 这就实现了服务的自动注册、发现、状态监控等

原理图:

  1. Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
  2. 提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
  3. 消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
  4. 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态 介绍了这么多, 开始实战吧

三. Eureka入门实战

开发工具: IDEA Spring Boot版本: 2.1.3 Spring Cloud版本: Greenwich

1.开始创建一个maven工程,进行多模块开发

File -> New -> Project - Maven

创建完结构如下

pom.xml文件先不用写东西 小伙伴等不及了吧 下面开始Spring Cloud

右击你的项目 New - Module - Spring Initializr

创建完之后 目录结构如下

我们来进行修改我们的pom文件

把我们刚刚创建的eureka-server服务下的pom文件

Spring Boot版本 以及一些属性复制粘贴到SpringCloudLearn的pom下 也就是父pom文件

然后eureka-server服务下的pom.xml继承父pom文件 如下进行更改

ps: 我比较喜欢把web依赖也放到父pom文件,因为我认为每个模块都必须要有

2.启动一个服务注册中心

只需要一个注解@EnableEurekaServer,这个注解需要在springboot工程的启动application类上加:

代码语言:javascript
复制
package com.li.eurekaserver;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer@SpringBootApplicationpublic class EurekaServerApplication {
    public static void main(String[] args) {        SpringApplication.run(EurekaServerApplication.class, args);    }
}

eureka是一个高可用的组件,它没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成),在默认情况下erureka server也是一个eureka client ,必须要指定一个 server eureka-server的配置文件 application.properties:

代码语言:javascript
复制
#服务注册中心端口号server.port=8761#服务注册中心实例的主机名eureka.instance.hostname=localhost#是否向服务注册中心注册自己eureka.client.register-with-eureka=false#是否检索服务eureka.client.fetch-registry=false#服务注册中心的配置内容,指定服务注册中心的位置eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server.

配置完成后 尝试启动项目,它是有界面的, 打开浏览器访问:http://localhost:8761

到这里服务注册中心就搞定了, 接下按照前面模拟的服务调用进行创建服务提供者以及服务消费者

创建一个服务提供者 (eureka client)

当client向server注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka server 从每个client实例接收心跳消息。 如果心跳超时,则通常将该实例从注册server中删除

步骤和前面的一样 依赖变了

创建完后pom改造后的

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.li</groupId>        <artifactId>SpringCloudLearn</artifactId>        <version>1.0-SNAPSHOT</version>        <relativePath>../</relativePath>    </parent>    <artifactId>eureka-client</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>eureka-client</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.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>
    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build>
</project>

通过注解@EnableEurekaClient 表明自己是一个eureka client

代码语言:javascript
复制
package com.li.eurekaclient;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication@EnableEurekaClientpublic class EurekaClientApplication {
    public static void main(String[] args) {        SpringApplication.run(EurekaClientApplication.class, args);    }
}

使用注解@EnableEurekaClient只是表明他是一个客户端, 我们还需要在配置文件中注明自己的服务注册中心的地址以及一些配置

application.properties:

代码语言:javascript
复制
# 端口号server.port=8762# 需要指明spring.application.name 这个很重要# 这在以后的服务与服务之间相互调用一般都是根据这个namespring.application.name=eureka-client#服务注册中心实例的主机名eureka.instance.hostname=localhost#服务注册中心端口号eureka.port=8761#在此指定服务注册中心地址eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${eureka.port}/eureka/

在程序入口定义一个接口

代码语言:javascript
复制
package com.li.eurekaclient;
import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication@EnableEurekaClient@RestControllerpublic class EurekaClientApplication {
    public static void main(String[] args) {        SpringApplication.run(EurekaClientApplication.class, args);    }
    @Value("${server.port}")    String port;
    @RequestMapping("/hello")    public String home(@RequestParam(value = "name", defaultValue = "lhd") String name) {        return "hello: " + name + " ,from port:" + port;    }
}

启动项目,打开浏览器访问:http://localhost:8761 eureka-client服务已经注册上去了

继续访问: http://localhost:8762/hello?name=lhd

可以正常访问 并返回结果

创建服务调用者从Eureka获取服务

创建consumer-server工程 步骤和eureka-client一模一样

在工程的启动类中,通过添加注解@EnableEurekaClient向服务中心注册

代码语言:javascript
复制
package com.li.consumerserver;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient@SpringBootApplicationpublic class ConsumerServerApplication {
    public static void main(String[] args) {        SpringApplication.run(ConsumerServerApplication.class, args);    }
}

配置application.properties:

代码语言:javascript
复制
# 端口号server.port=8763# 需要指明spring.application.name 这个很重要# 这在以后的服务与服务之间相互调用一般都是根据这个namespring.application.name=consumer-server#服务注册中心实例的主机名eureka.instance.hostname=localhost#服务注册中心端口号eureka.port=8761#在此指定服务注册中心地址eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${eureka.port}/eureka/

在程序入口定义接口并且向程序的ioc注入一个bean: restTemplate 用DiscoveryClient类的方法,根据服务名称,获取服务实例 restTemplate来消费eureka-client服务的“/hello”接口

代码语言:javascript
复制
package com.li.consumerserver;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.context.annotation.Bean;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController@EnableEurekaClient@SpringBootApplicationpublic class ConsumerServerApplication {
    public static void main(String[] args) {        SpringApplication.run(ConsumerServerApplication.class, args);    }
    @Bean    RestTemplate restTemplate() {        return new RestTemplate();    }
    // eureka客户端,可以获取到eureka中服务的信息    @Autowired    private DiscoveryClient discoveryClient;
    @Autowired    RestTemplate restTemplate;
    @RequestMapping("/hello")    public String hello(String name) {
        // 根据服务名称,获取服务实例。有可能是集群,所以是service实例集合        List<ServiceInstance> instances = discoveryClient.getInstances("eureka-client");        // 因为只有一个eureka-client 所以获取第一个实例        ServiceInstance instance = instances.get(0);        // 获取ip和端口信息,拼接成服务地址        String baseUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/hello?name="+name;        return restTemplate.getForObject(baseUrl, String.class);    }
}

启动程序 打开浏览器访问: http://localhost:8761/ consumer-server服务已经注册进来

继续访问: http://localhost:8763/hello?name=lhd

这样就可以调用成功

Debug跟踪运行:

生成的URL:

Eureka详解

服务注册中心

Eureka的服务端应用,提供服务注册和发现功能,就是刚刚我们建立的eureka-server

服务提供者

提供服务的应用,可以是SpringBoot应用,也可以是其它任意技术实现,只要对外提供的是Rest风格服务即可。本例中就是我们实现的eureka-client

服务消费者

消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。本例中就是我们实现的consumer-server

SpringCloud Eureka服务发现与注册讲解完毕

源码下载: https://github.com/LiHaodong888/SpringCloudLearn

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 李浩东的博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 现在我们需要模拟一个服务调用的场景, 方便后面学习微服务架构
  • 二. Eureka注册中心
  • 三. Eureka入门实战
    • 1.开始创建一个maven工程,进行多模块开发
      • 2.启动一个服务注册中心
      • 创建一个服务提供者 (eureka client)
      • 创建服务调用者从Eureka获取服务
      相关产品与服务
      微服务引擎 TSE
      微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档