初学SringCloud:建设EurekaClient集群(新建一个服务提供者模块,以8002为端口)

我的上一篇博客:

初学SpringCloud:建设EurekaServer集群(新建一个EurekaServer模块)

1、背景介绍

我的这个专栏记录我初学SpringCloud时,动手做的一个简单的微服务架构的项目。目前项目使用的是Eureka的技术,已经创建好了EurekaServer的集群,并且做好了配置(只有两台EurekaServer,也就是两个微服务模块),端口分别是7001和7002。已经创建好了一个服务提供者的EurekaClient模块,端口是8001。已经创建好了一个服务调用者模块,端口是90。服务调用者模块可以通过RestTemplate类发送HTTP请求,调用8001为端口的模块的业务逻辑服务。8001端口的模块和90端口的模块,都属于EurekaClient,目前都已经做好了注册进7001端口的EurekaServer的配置。

2、本篇博客的目的

我的上一篇博客中,创建好了EurekaServer的集群。同样是在这个专栏,我有一篇博客粗略的陈述了我对建设集群的看法。简单的来说,建设集群就是为了实现高可用。既然EurekaServer可以集群化,EurekaClient当然也是可以集群化的。本篇博客就再创建一个以8002为端口的服务提供者模块,跟8001为端口的服务提供者模块一起组成一个小集群。

3、新增一个8001为端口的服务提供者微服务模块,展示一下这个模块的文件结构。

 由于是一个服务提供者的模块,是一个集群的组成部分,目的是为了提高可用性,实现高可用。因此这个模块的代码和文件结构,甚至是GetMapping注解的value值,跟8001端口模块的都是一样的。一定程度上来说,8001端口模块和这个8002端口模块,二者就几乎是一样的。

4、简单解释一下这个模块的代码

由于这个模块是服务提供者的模块,是含有业务逻辑代码的,因此它就肯定是有数据库的相关配置的。

首先就是mapper.xml文件,这个文件夹放在了resources下面,因此在application文件中是需要配置一下的。由于我使用的是MyBatis框架,因此这个文件就是将我们dao层接口的方法跟SQL语句,还有公共模块的实体类做一下关联。

再向上,就是Service层,首先是一个接口,这个接口还是两个方法。然后还有就是一个实现类,这个实现类就是需要dao层接口的注入,使用@Resource注解,直接返回的就是dao层的结果,当然需要传入必要的形参。

再向上一层,就是controller层。这一层是真正的业务逻辑层。它需要service层的注入,注入实现类。然后调用方法,得到返回结果。根据返回结果的不同,再作出相应的判断。由于我们使用的是前后端分离的设计架构,因此作出的相应的判断,就是返回一个JSON串,一个通用返回结果的类的JSON串,当然需要是一个泛型类。

最后就是我们的重要的配置文件,application文件配置一下数据库,Eureka,端口,MyBatis的mapper文件。还有是我们的POM文件,引入必要的依赖。

由于我在前面的博客中,已经详细的讲述了8001为端口的模块的代码,二者代码几乎就是一样的,这里我就不详细记录了,我直接把代码粘贴到下面来了。

     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属性 强调的是我们需要映射那一个dao接口-->
<mapper namespace="com.springcloud.dao.PaymentDao">
    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>
    <resultMap id="BaseResultMap" type="com.springcloud.entities.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 payment where id=#{id};
    </select>
</mapper>

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">
    <parent>
        <artifactId>cloud2023</artifactId>
        <groupId>com.lanse.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8002</artifactId>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <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>


        <!--下面的这个依赖是引入了自己的那个模块新建成的jar包,就是把通用的entities提出来了-->
        <dependency>
            <groupId>com.lanse.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>

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

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


        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

        <!--加了下面这个依赖以后,application文件就可以正常识别了-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.4</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>

</project>

 dao层代码

package com.springcloud.dao;

import com.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

//dao层次就是写增删改查的方法
//@Mapper
//上面的这个注解相当于  @Repository,好像是说比这个要更好
@Mapper
public interface PaymentDao {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") Long id);

}

service层接口和实现类

package com.springcloud.service;


import com.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;

public interface PaymentService {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") Long id);
}
package com.springcloud.service.impl;

import com.springcloud.dao.PaymentDao;
import com.springcloud.entities.Payment;
import com.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class PaymentServiceImpl implements PaymentService {
    @Resource
    //@Autowired
    //上面这个注解的意思,还需要再认真的看一看
    //就是类似于@Autowired
    private PaymentDao paymentDao;
    @Override
    public int create(Payment payment) {
        return paymentDao.create(payment);
    }
    @Override
    public Payment getPaymentById(Long id) {
        return paymentDao.getPaymentById(id);
    }
}

controller层代码

package com.springcloud.controller;

import com.springcloud.entities.CommonResult;
import com.springcloud.entities.Payment;
import com.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
//下面这个注解的作用我还不是很清楚
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

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

    @GetMapping("/payment/get/{id}")
    @ResponseBody
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        if (payment!=null){
            return new CommonResult(200,"查询数据成功,port:"+serverPort,payment);
        }else{
            return new CommonResult(500,"查询数据是空",null);

        }
    }

    @PostMapping("/payment/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
//        log.info("插入成功"+result);
        if (result>0){
            return new CommonResult(200,"插入数据成功,port"+serverPort,result);

        }else{
            return new CommonResult(500,"插入数据失败",null);

        }
    }

    @GetMapping("/payment/lb")
    public String getPaymentLb(){
        return serverPort;
    }
}

5、单独看一下application文件

application文件的代码如下所示:

server:
  port: 8002

spring:
  application:
    name: cloud-payment-service
    #上面这个是微服务的名字,是不能随便改变的,尽量不要随便改变。作为EurekaClient是需要到EurekaServer去注册的
    #注册的时候,就是要用到这个名字的
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong
    username: root
    password: 123456

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.springcloud.entities

eureka:
  client:
    register-with-eureka: true #这个就表示是否默认注册到EurekaServer,当然就是true啦
    fetch-registry: true #这个表示是否从EurekaServer抓取已有的注册信息,默认是true。
    #其实上面的这个属性单节点是无所谓的,但是集群就必须设置为true,这样才能配合ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka ,http://eureka7002.com:7002/eureka
  instance:
    instance-id: payment8002 #这个会使得访问EurekaServer的时候,Status一栏,就会显示它
    prefer-ip-address: true #这个就是会使得访问的时候显示IP地址,更加的方便一些
#    service-url:
#      defaultzone: http://localhost:7001/eureka
#    service-url:
#      defaultZone: http://localhost:7001/eureka


首先需要配置的就是端口号,我按照事先的约定,配置了8002

然后配置了 spring.application.name    这里我想重点说一下:现在配置的是EurekaClient服务提供者的集群,它的每一个组成部分,都是要注册进EurekaServer中去的。但是呢,作为一个集群,将来肯定是以集体的面目呈现在系统中的,那么怎样才是集体呢?最直接的方式就是都有一样的名字。这就是spring.application.name属性的作用,它就像是连锁店的招牌一样,虽然店的地理位置不一样,但是招牌都是一样的,无论去哪一家店,提供的服务都是一样的。

再往下就是配置数据库了,这里我就不多说了。

再往下,就是需要配置一下mapper文件和我们公共模块的实体类。

再往下的两个属性,注册到注册中心啊,抓取注册表啊,都可以设置为ture

再外下,是一个 service-url.defaultZone属性,这个属性我们把两个注册中心都写了上去。其实是可以只写一个的,因为EurekaServer集群是会每隔一段时间互相更新服务注册表的。但是都写上去明显是更保险的。注意一下,其中的  eureka7001.com和eureka7002.com,都是EurekaServer的hostname属性的值。

再往下,有一个  instance-id属性。想象一下,即使是连锁店,即使招牌是一样的,但是每一家店总归是有编号的吧,总归是可以区分的吧,总归在总公司的名单上是可以区分的吧。这个instance-id属性,就是使得注册进EurekaServer以后,每台EurekaClient依然有唯一的id,是可以区分的。

6、我们一次启动EurekaServer的两台机器:7001端口模块,7002端口模块;再启动EurekaClient服务提供者的两台机器:8001端口模块,8002端口模块;再启动EurekaClient服务调用者(消费者)90端口模块

到此,项目的8002端口的EurekaClient服务提供者模块已经创建完毕了,并且所有的配置也是都已经到位了。

按照顺序,依次启动服务。启动后的截图如下所示:

7、访问7001端口和7002端口的EurekaServer的浏览器页面,观察注册结果

 

明显的,可以从注册结果中看到,它们两个是相互守望者的了,就像是有一条网线相连一样。

并且,所有的微服务模块,也都在它们两个的服务注册表上有了。

8、提醒一下现在的  90端口

由于我们现在已经将服务提供者端改造成了集群的形式,它们对外有一个统一的  招牌。这个  招牌

就是  它们application文件中的 spring.application.name属性的值。

因此,我们是需要修改  90端口的服务消费者的controller层的RestTemplate类发送的HTTP请求的内容的, 就是将原来的写死的: http://localhost:8001  修改为  http://CLOUD-PAYMENT-SERVICE     跟服务提供者模块的application文件中的spring.application.name属性的值保持一致。

9、总结

目前已经完成了集群的改造。一切都是可以正常运行的,这里我就不多展示了。

大家有没有一个疑问,当我们通过90端口的服务消费者模块,调用服务提供者模块的业务逻辑代码的时候,内部究竟会去8001端口还是8002端口呢?

这里就牵扯到一个问题了:  负载均衡。  计算机,只是一个机器,绝对不会说是随心执行指令的。即使是随机,它的内部也是有产生随机码的函数的。  负载均衡,就是说根据一定的规则,来决定究竟调用8001端口的模块还是8002端口的模块。

提前剧透一下,默认的  负载均衡原则是  轮询。Eureka内部是已经集成了的。可以在Maven里面,看到下面的依赖包含的关系。

我后面的博客会记录一下使用ZooKeeper作为服务注册中心的代码:

初学SpringCloud:使用ZooKeeper作为服务注册中心,搭建单机版ZooKeeper服务注册中心 之 在CentOS下安装JDK

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你是我的日月星河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值