集群Eureka环境构建

该文章详细介绍了如何构建Eureka注册中心集群以提高服务的高可用性,包括在EurekaServer的配置、服务提供者注册、服务消费者调用等方面。同时,文章展示了如何通过负载均衡实现微服务间的均衡调用,并通过实际的微服务示例(如支付服务)来演示整个过程。
摘要由CSDN通过智能技术生成

集群原理说明

问题:微服务RPC远程服务调用最核心的是什么 答:高可用,试想你的注册中心只有一个only one, 它出故障了(不能提供和服务了),会导致整个为服务环境不可用,所以解决办法:搭建Eureka注册中心集群 ,实现负载均衡+故障容错。

集群环境的操作实现原理:互相注册,相互守望,对外是整体。

集群环境构建

一、新建cloud-eureka-server7001

  1. 更改pom文件
<dependencies>
    <!--eureka-server(增加内容)-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <!-- 引入自定义的api通用包,可以使用Payment和CommonResult -->
    <dependency>
        <groupId>com.chenxin.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </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>
    <!--一般通用配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
</dependencies>
  1. 创建YAML文件
server:
  port: 7001

eureka:
  instance:
    hostname: localhost #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 创建主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableEurekaServer // 标识当前服务是EurekaServer
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}
  1. 测试Euraka是否部署成功

访问页面 http://localhost:7001/

二、新建cloud-eureka-server7002(略)

更改端口号和名称即可

三、创建微服务提供者支付Module模块,EurekaClient端8001入驻EurekaServer成为服务提供者

  1. 改pom(新增eureka-client坐标)

<dependencies>
<!--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>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.12</version>
    </dependency>
    <!--mysql-connector-java-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--jdbc-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  1. 写YAML

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver             # mysql驱动包 com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&useSSL=false
    username: root
    password: root


mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.chenxin.springcloud.entity    # 所有Entity别名类所在包

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://eureka-server:7001/eureka
  1. 创建主启动类

@SpringBootApplication
@EnableEurekaClient // 添加注解标识为EurekaClient
public class Payment8001 {
    public static void main(String[] args) {
        SpringApplication.run(Payment8001.class,args);
    }
}
  1. 建表SQL

CREATE TABLE `payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `serial` varchar(200) DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

实体Payment

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

Json封装体CommonResult

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String  message;
    private T data;
    public CommonResult(Integer code, String message) {
        this(code,message,null);
    }
}

Dao层

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

@Mapper
public interface PaymentMapper {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") Long id);
}

mybaits的映射文件PaymentMapper.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.chenxin.springcloud.dao.PaymentMapper">

    <resultMap id="BaseResultMap" type="Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO payment(SERIAL) VALUES(#{serial});
    </insert>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap" >
        SELECT * FROM payment WHERE id=#{id};
    </select>

</mapper>

Service层

public interface PaymentService {
    public int create(Payment payment);
    public Payment getPaymentById(@Param("id") Long id);
}

ServiceImpl

import com.chenxin.springcloud.dao.PaymentMapper;
import com.chenxin.springcloud.entity.Payment;
import com.chenxin.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class PaymentServiceImpl implements PaymentService {
    @Resource
    private PaymentMapper paymentMapper;
    @Override
    public int create(Payment payment) {
        return paymentMapper.create(payment);
    }

    @Override
    public Payment getPaymentById(Long id) {
        return paymentMapper.getPaymentById(id);
    }
}

Controller

import com.chenxin.springcloud.entity.Payment;
import com.chenxin.springcloud.service.PaymentService;
import com.chenxin.springcloud.utils.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);

        if(result > 0) {
            return new CommonResult(200,"插入数据库成功",result);
        }else{
            return new CommonResult(444,"插入数据库失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("*****查询结果:{}",payment);
        if (payment != null) {
            return new CommonResult(200,"查询成功",payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }
}

5.测试

访问http://localhost:8001/payment/get/1

三、EurekaClient端80入驻EurekaServer成为服务消费者

1.创建模块cloud-consumer-order80

改pom.xml

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

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.写YAML

server:
  port: 80

3主启动类

import org.springframework.boot.SpringApplication;

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

4.实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private Integer code;
    private String  message;
    private T data;
    public CommonResult(Integer code, String message) {
        this(code,message,null);
    }
}

5.将 RestTemplate 注入到 Spring Boot 容器中

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
    // @Bean相当于applicationContext.xml <bean id="" class="">
}

6.Controller

import com.chenxin.springcloud.entity.Payment;
import com.chenxin.springcloud.utils.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {
    public static final String PAYMENT_URL = "http://localhost:8001";
    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment) {
        log.info("进入了客户端/消费者端的create方法...");
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create",payment, CommonResult.class);
    }

    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Integer id) {
        log.info("进入了客户端/消费者端的getPayment方法...");
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id,CommonResult.class);
    }
}

7.测试

直接访问服务端:http://localhost:8001/payment/get/1

通过消费者端访问:http://localhost/consumer/payment/get/1

四、新建cloud-api-commons

  1. pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 处理日期时间好用:https://www.cnblogs.com/wuwuyong/p/16529839.html -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.1.0</version>
    </dependency>
</dependencies>
  1. Entity & CommonResult

在新建的模块(cloud-api-commons)新建同样的包结构,并将原来的重复代码类复制过来,

如下图所示:

  1. maven命令clean install(略)

订单80和支付8001分别改造

删除各自的原先有过的entity和utils包

各自粘贴POM内容

<!-- 引入自定义的api通用包,可以使用Payment和CommonResult -->
<dependency>
    <groupId>com.chenxin.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>
五、测试集群

启动7001:

启动7002:

访问:http://localhost:7001/

访问:http://localhost:7002/

访问:由于做了域名映射,可以访问:http://eureka7001.com:7001/

访问:由于做了域名映射,可以访问:http://eureka7002.com:7002/

1、将支付微服务8001注册到2台Eureka集群中

修改 application.yml

service-url:
   # defaultZone: http://localhost:7001/eureka
   defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

2、将支付微服务80注册到2台Eureka集群中

修改 application.yml

service-url:
  #defaultZone: http://localhost:7001/eureka
  defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

3、测试

先要启动EurekaServer,7001/7002服务

再要启动服务提供者provider,8001

再要启动消费者,80

http://localhost/consumer/payment/get/1

4、支付微服务(8001)集群配置

新建cloud-provider-payment8002(略)
改pom(直接复制8001的pom)(略)
写YAML(直接复制8001的YAML)(略)
主启动(略)
业务类(略)
修改8001&8002的Controller
// -----------新增代码开始-----------------
@Value("${server.port}")
private String serverPort;
// -----------新增代码结束-----------------
// 以下完整代码
@RestController
@Slf4j
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;
    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);

        if(result > 0) {
            // ----------- 修改位置——新增了显示服务的端口 ------------
            return new CommonResult(200,"插入数据库成功,服务端口" + serverPort,result);
        }else{
            return new CommonResult(444,"插入数据库失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("*****查询结果:{}",payment);
        if (payment != null) {
            return new CommonResult(200,"查询成功,服务端口" + serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }
}
5、测试一(启动顺序)

启动7001

启动7001

启动8001

启动8002

启动80

启动后查看集群环境是否正常:

测试二(访问资源)

访问8001:http://localhost:8001/payment/get/1

访问8002:http://localhost:8002/payment/get/1

通过消费者端访问:http://localhost/consumer/payment/get/1

6、负载均衡(解决只从一个服务器获取资源)
Bug(订单服务访问地址不能写死)

由于我们现在在注册中心已经有了服务提供者,上图中我们不使用localhost,而是使用我们注册中心的服务名,即:

public static final String PAYMENT_URL = "http://localhost:8001";

改为

public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

修改完毕重启80服务:

@LoadBalanced注解负载均衡的能力

使用@LoadBalanced注解赋予RestTemplate负载均衡的能力

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力,这样才可以通过RestTemplate负载均衡方式调用微服务
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
    // @Bean相当于applicationContext.xml <bean id="" class="">
}
测试(修改完毕,重新启动80进行测试)
// 多次刷新会从不同的服务器上获取资源
{"code":200,"message":"查询成功,服务端口8002","data":{"id":1,"serial":"123DAF234SDFA342D"}}
{"code":200,"message":"查询成功,服务端口8001","data":{"id":1,"serial":"123DAF234SDFA342D"}}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值