二、Eureka服务注册与发现

SpringCloud系列目录:

Eureka虽然已经停更进入维护阶段,但是依旧不能阻挡大家想一睹芳容的心,归根结底还是太优秀,打败天下无敌手~~~~~ ,赶脚自己与段子手之间的距离越来越近(✪ω✪)


现在不管是啥,都得有背景。写个文实在是太难了,有料还得看的爽 (╥╯^╰╥) 当然这些都是题外话,下面进入正题

写了这么久的csdn才知道还可以在markdown中设置图片的格式。。。。。。?戳这里


Eureka属于CA的设计,ZK、Consul等都是CP的设计

服务治理

SpringCloud封装了Netflix公司开发的Eureka模块实现服务治理
在传统的Rpc远程调用框架中,管理每个服务和服务之间的依赖关系比较复杂,管理比较复杂,所以使用服务治理,管理服务与服务之间依赖关系,可以使用服务调用、负载均衡、容错。

什么是服务注册与发现

Eureka采用了CS的架构设计**,Eureka Server作为服务注册功能的服务器**,它是服务注册中心。而系统中的其他微服务,使用Eureka client连接到Eureka Serve并维持心跳连接。这样系统的维护人员就可以通过Eureka Serve来监控系统中各个微服务是否正常运行。
在服务注册与发现中个,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息,比如服务器通讯地址等以别名的方式注册到注册中心上。另一方(消费者|服务者)以该别名去注册中心上获取实际的服务通讯地址,然后再实现本地RPC调用。RPC远程调用核心架构设计思想在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理的概念)。任何RPC远程框架中,都会有一个注册中心(存放服务器地址相关信息)
在这里插入图片描述

Eureka两组件

Eureka包含两组件:Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表将会存储所有可用服务节点的信息,服务街店的信息可以再界面中直观看到。
Eureka通过注册中心进行访问
Eureka Client是一个Java客户端(当然这个客户端也可是使用其他语言实现),客户端同时也具备一个内置的、使用轮询(Round-robin)负载算法的负载均衡器(默认使用轮询算法)。在应用启动后,将会向Eureka Server发送心跳(默认周期30秒)。如果Eureka Server再多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)

最新版本的Eureka依赖坐标已经将Eureka Server和Eureka Client分开


这里使用一个商场的案例给大家分享一下服务端和客户端之间的关系:
商场这个服务我们可以把它看做是Eureka Servie、那么其中入驻的商户便是Eureka Client,顾客在逛商场的时候可以通过商场提供的导引提示找到对应的商户(服务),入驻的商户需要定时想商场付管理费用,定时给钱这个过程就是想Eureka Server发送心跳的过程;如果入驻的商户在一定的时间段内没有付管理费用,那么商场就回把商场中对应的商家导引去掉,这个过程就是把服务注册表中服务节点移除的过程

一、代码示例

光说不干假把式,下面将构建Eureka大保健(`・ω・´)
在构建项目之前先说明一下,示例代码maven架构是使用的聚合,其中的父工程我就不再演示,后续代码我会提交到github

1.1 创建一个父工程

父工程中主要修改pom.xml配置文件即可,要注意的是打包方式选择pom

<?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>

    <groupId>com.gaowz</groupId>
    <artifactId>tx_springcloud_02</artifactId>

    <version>1.0-SNAPSHOT</version>
    
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.49</mysql.version>
        <druid.version>1.1.21</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>

    <!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version  -->
    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba 2.1.0.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <!--热部署插件-->
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>
1.2 创建基础服务(这一步可以根据自己的实际情况创建,也可以自行整合到对应的服务中)

基础服务的创建方式跟上述module的创建方式相同:
此基础服务中只有两个实体类,pom.xml也不需要配置(^_−)☆,module的结构:
在这里插入图片描述
创建的过程我不在重复,以下是需要的代码

创建CommonResult.java


public class CommonResult<T> implements Serializable {
    // 404 not_found
    private Integer code;
    private String  message;
    private T       data;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public CommonResult(){

    }
    public CommonResult(Integer code, String message){
        this(code,message,null);
    }
    public CommonResult(Integer code, String message, T data){
        this.code = code;
        this.message = message;
        this.data = data;
    }
}

创建Payment.java

public class Payment {
    private Long id;
    private String serial;

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getSerial() {
        return serial;
    }
    public void setSerial(String serial) {
        this.serial = serial;
    }
}

1.3 构建单机Eureka

1)新建module

在父工程下新建一个cloud-eureka-server7001,这个服务可以理解为商场,项目结构如下所示:
在这里插入图片描述
2)改pom,添加依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- boot web actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
    </dependencies>

3)写yml配置文件
在resource目录下创建yml配置文件

server:
  port: 7001
eureka:
  instance:
    hostname: eureka7001.com  #eureka服务端的实例名字
  client:
    register-with-eureka: false    #表示不向注册中心注册自己 商场不需要向自己支付服务费用
    fetch-registry: false   #表示自己就是 注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
 

注:此处defaultZone我有在hosts中配置过,在C:\Windows\System32\drivers\etc中添加如下配置即可
在这里插入图片描述
4)主启动类

新建主启动类,服务的入口,这里不要忘记添加Eureka的Server端注解

@EnableEurekaServer
@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}

5)测试

服务启动成功之后,便可以直接访问我们的7001服务,如下就是Erueka的主页面,当然目前还没有服务注册进来,所以我们在这个界面中看不到任何的服务
在这里插入图片描述
到这里,单机版的Eureka Server就搭建完成了,接下来就需要编写商户的服务,我们需要编写一个商户的服务并注册到7001中,这样用户就可以在7001(商场)中看到对应的服务信息

1.4 构建一个Eureka Client端

1)创建cloud-provider-payment8001并注册进Eureka Server成为服务提供者

cloud-provider-payment8001的项目结构如下:
在这里插入图片描述
注:8001服务还用到上述基础服务,这个基础服务中主要是封装的返回给前端的实体,当然你在使用的时候也可以整合到8001的服务中:
2)修改pom.xml 文件

刚刚有说过,Eureka有将Eureka Client 和Eureka Server 的依赖分开,其实在你添加依赖的时候就已经发现了

  <dependencies>
        <!--自定义的依赖,此处的依赖即是上述所提到的依赖-->
        <dependency>
            <groupId>com.gaowz</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--引入Eureka client端-->
        <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-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--配置切面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!--引入swagger注解-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>

        <!--配置日志切面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version>
        </dependency>

    </dependencies>

3)写yml配置文件

8001需要使用mybatis从数据库中查询一条数据,所以需要配置数据库与Mybatis的相关信息,当然如果你如果只是想测试Eureka服务,你完全可以只写Controller

#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8001

spring:
  application:
    #微服务名称 此处的名称即为注册发现中心的名称
    name: cloud-payment-service
  #数据库配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/springcloud_dev?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

#与mybatis整合的配置信息
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.gao.domain  #所有Entity别名类所在包

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
  #这个注解此时暂时不需要关注
  instance:
    instance-id: payment8001
    prefer-ip-address: true

关于Mybatis和数据库的配置这里没有什么好说的,主要还是在Eureka的配置上

  • name: cloud-payment-service:表明在注册中心8001的服务名称
  • register-with-eureka: true:表示将自己注册到Eureka Server上
  • fetch-registry: true:表示从Eureka Server获取注册的服务信息
  • defaultZone:注册的服务地址


4)主启动
新增主启动类,此处要注意的是,主启动类要与Spring扫描的package同级,否则会出现,bean实例化失败的情况
这里新增了两个注解:@EnableEurekaClient @EnableDiscoveryClient,关于这两个注解的用法请自行查询,这里不再重复(✪ω✪)

@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentApplication8001 {

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

5)编写对应的业务类:
新建PaymentController.java

@RestController
@RequestMapping("/payment")
public class PaymentController {

    //日志使用
    private static Logger _log = LoggerFactory.getLogger(PaymentController.class);

    @Value("${server.port}")
    private String serverPort;

    @Autowired
    private PaymentServiceImpl paymentService;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/get/{id}")
    public CommonResult getPaymentById(@PathVariable(value = "id") Long id) {
        Payment payment = paymentService.getPaymentById(id);
        if (StringUtils.isEmpty(payment)) {
            return new CommonResult(404, "查无数据,serverPort "+serverPort, null);
        } else {
            return new CommonResult(200, "查询成功,serverPort "+serverPort, payment);
        }
    }

    @GetMapping(value = "/discovery")
    public Object discovery(){

        List<String> services = discoveryClient.getServices();
        for (String service : services) {
            _log.info("*****"+service);
        }
        //输出实例信息
        List<ServiceInstance> instance = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance serviceInstance : instance) {
            _log.info("*****"+serviceInstance.getHost()+'\t'+serviceInstance.getInstanceId());
        }
        return this.discoveryClient;
    }

    /**
     * 超时调用
     * @return
     */
    @GetMapping(value = "/feign/timeout")
    public String paymentFeiginTimeout(){
        try {
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return serverPort;
    }

}

创建持久层接口PaymentDao.java

@Mapper
public interface PaymentDao {

    int addPayment(Payment payment);

    Payment getPaymentById(Long ID);

}

创建IPaymentService .java 接口,并创建对应的实现类

public interface IPaymentService {

    Payment getPaymentById(Long id);

    int addPayment(Payment payment);
}

创建支付服务的实现类PaymentServiceImpl.java

@Service
public class PaymentServiceImpl implements IPaymentService {


    @Resource
    private PaymentDao paymentDao;

    @Override
    public Payment getPaymentById(Long id) {
        Payment payment = paymentDao.getPaymentById(id);
        return payment;
    }

    @Override
    public int addPayment(Payment payment) {
        int i = paymentDao.addPayment(payment);
        return i;
    }
}

创建对应的Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.gao.dao.PaymentDao">

    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into t_payment(serial) values (#{id});
    </insert>

    <!--mapper不做映射其实也可以,只不过方便后续的字段的对应-->
    <resultMap id="BaseResultMap" type="com.gao.domain.Payment">
        <id column="id" property="id" jdbcType="BIGINT"></id>
        <id column="serial" property="serial" jdbcType="VARCHAR"></id>
    </resultMap>

    <select id="getPaymentById" parameterType="long" resultMap="BaseResultMap">
        select * from t_payment where id =#{id}
    </select>

</mapper>



另外,这里的表结构比较简单,其中只有两个字段,如果有需要可以自行创建
在这里插入图片描述


6)创建完成之后我们启动8001服务
在这里插入图片描述
看起来启动时没有问题的,我们测试一下是否可以查询到数据
在这里插入图片描述
查询成功之后我们可以再Eureka Server中查询一下是否已经注册到Server中

在这里插入图片描述
看到这个就说明已经将8001服务注册到Eureka Server中了,到这里我们的商场和商户已经新建完成了。那还缺少用户,用户需要通过商场找到对应的商户。
在这里插入图片描述
源码地址

欲知后事如何,且看下回分解~ 您的点赞就是对小编最大的支持


版权声明:本文为gwz_6903原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。