Spring Cloud微服务开发笔记2——Eureka集群搭建

Stella981
• 阅读 777

上一篇博文中,我们介绍了如何搭建一个Eureka服务的架构,但是服务提供者我们只用了一个单例,完全不能体现高并发高可用。本文我们尝试在上一篇文章示例Eureka项目的基础上继续完善,让它可以做到一个集群的部署。

Eureka集群架构

我们先看一下我们这次示例打算改造成的架构图:

Spring Cloud微服务开发笔记2——Eureka集群搭建

在我们的Eureka服务器里面会启动两个实例,这两个实例会相互注册。

然后服务提供者也会启动两个实例,这两个实例都会注册到我们服务器的两个实例,是的,像图中那样一个服务提供者实例分别向两个服务器实例注册。

服务调用者也会注册到两个服务器实例上面。

最后我们会编写一个Rest客户端,去调用服务调用者的站点端口,来测试服务调用的过程。

我们还用上文的比方继续比喻,Eureka服务器相当于114电话查询平台,而两个Eureka服务器实例相当于两个114接线员。某某酒店现在要提供电话服务,于是在114注册,它就是服务提供者。由于业务扩大,十分繁忙,我们需要提供接听电话的效率和及时性,类似于服务提供者需要高并发高可用,于是酒店也弄了两个接线员,相当于两个服务提供者实例。而一个客户想请求酒店服务,首先回去114查询这家酒店的电话,但是114是哪位接线员提供的服务信息和电话号码,我们不关心;同样,客户拿到酒店电话后,自行打电话给酒店,至于酒店是哪位客服接电话的,客户也不关心,反正联系到酒店了。

Eureka集群项目配置

模拟准备

由于我们是在本地电脑上模拟有这么个集群,所以我们需要在host文件里做个配置:

我们打开C:\Windows\System32\drivers\etc\host,加入下面这行,假装我们有两个节点机器slave1和slave2。无论我们等会儿访问slave1或是slave2,其实访问的都是我本地的IP地址。

    127.0.0.1    slave1 slave2

(本文出自oschina博主happyBKs的博文:https://my.oschina.net/happyBKs/blog/1631960)

Eureka服务器的集群配置

今天的架构中,需要两个Eureka实例,并且这两个Eureka服务器之间还相互注册了信息。所以我们需要修改一下上文中的Eureka服务器应用项目eurekaServer。

我们将eurekaServer项目的配置文件改成如下,注意我们配置了两个profile,当中用---分隔开,我们在启动springboot的应用时只要将对应的profile名称作为参数赋值给springboot程序就可以了。

之前的application.yml

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

现在我们改成:

配置了两个eureka,计划通过profile来标识它们,然后以profile作为启动参数来启动不同配置的eureka实例应用。这两个eureka分别向对方注册自己的信息。注意,我这里设置了不同的端口号。因为虽然看似它们的IP域名为slave1和slave2,其实是我本地模拟只有一台PC,没办法修改host文件假装是在两个IP的机器上,其实还是在一台机器上,所以端口号必须不同。真实生产有两台服务器,端口号不妨一样都设置为8761.

server:
  port: 8761
spring:
  profiles: eureka1
eureka:
  client:
    service-url:
      defaultZone: http://slave2:8762/eureka
---
server:
  port: 8762
spring:
  profiles: eureka2
eureka:
  client:
    service-url:
      defaultZone: http://slave1:8761/eureka
    
      

注意,可以配置一个应用名称,通过配置项spring.application.name

启动类我们做个改动,我们希望输入一个profile参数,以按照配置中不同的profile来分别启动一个eureka实例。

package com.happybks.demo;

import java.util.Scanner;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        final String profile = sc.nextLine();
        new SpringApplicationBuilder(EurekaServerApplication.class).profiles(profile).run(args);
        sc.close();
    }
}

服务提供者客户端应用配置

application.yml配置文件

spring:
  application:
    name: first-service-app
eureka:
  client:
    service-url:
      defaultZone: http://slave1:8761/eureka/,http://slave2:8761/eureka/

服务请求者first-service-app需要同时向两个eureka服务器发出注册信息,所以这里配置了两个eureka的地址信息,并且用逗号隔离开。

然后我们改写一下上一篇例子中的控制器和bean。

PetBean增加一个info字段。

package com.happybks.beans;

public class PetBean {
    private String breedType;
    private int age;
    private double price;
    private String info;
    public String getBreedType() {
        return breedType;
    }
    public void setBreedType(String breedType) {
        this.breedType = breedType;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
    public PetBean() {
        
    }
    @Override
    public String toString() {
        return "PetBean [breedType=" + breedType + ", age=" + age + ", price=" + price + ", info=" + info + "]";
    }
    
}

控制器的接口方法增加一个servlet的request参数。(如有其它参数,可以继续追加在一起不要紧)通过reuqest获取请求的url地址,就可以知道别人发给自己的请求时的url地址,也就知道了自己的IP服务地址。这个信息我们存入petBean的info字段返回json中给前端,我们计划看看在一个eureka集群中,一会儿服务调用者向同一个服务的发送请求时,该服务的两个服务提供者实例谁来应答。

package com.happybks.controllers;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.happybks.beans.PetBean;

@RestController
public class PetInfoController {
    
    @GetMapping(value="/petinfo/findpet",produces=MediaType.APPLICATION_JSON_VALUE)
    public PetBean findPet(HttpServletRequest request) {
        PetBean petBean = new PetBean();
        petBean.setBreedType("聪明机灵小柴犬");
        petBean.setAge(1);
        petBean.setPrice(50000);
        petBean.setInfo(request.getRequestURL().toString());
        return petBean;
    }

}

启动类:

这里我们秀另一种区别参数,可以借助于赋予不同的端口,而不是profile来启动多个实例。

package com.happybks.providers;

import java.util.Scanner;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;


@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages = { "com.happybks.controllers" })
public class ServiceProvider1Application {

    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);
        String port=sc.nextLine();
        new SpringApplicationBuilder(ServiceProvider1Application.class).properties("server.port="+port).run(args);
        sc.close();
    }
}

配置服务调用者

这里我们沿用上次的控制器,一会儿我们请求这个控制器的方法url,它会向服务提供者发出一个请求。

package com.happybks.controllers;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

@RestController
@Configuration
public class ShopperBehaviorController {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
    
    @GetMapping("/shopper/routine")
    public String routine() {
        RestTemplate restTemplate = getRestTemplate();
        String json = restTemplate.getForObject("http://first-service-app/petinfo/findpet", String.class);
        return json;
    }
    
    @GetMapping("/shopper/getservice/{id}")
    public String getservice(@PathVariable("id") Integer id, HttpServletRequest request) {
        RestTemplate restTemplate = getRestTemplate();
        String json = restTemplate.getForObject("http://first-service-app/petinfo/findpet", String.class);
        System.out.println(request.getRequestURL().toString());
        return json;
    }
}

启动类:

package com.happybks.callers;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = { "com.happybks.controllers" })
@EnableEurekaClient
public class ServiceCaller1Application {

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

关于项目的其他代码和配置,请参见上一篇博客,我本文就不全部粘贴出来了。

启动集群

下面,我们来启动各个服务:

启动两个相互注册的EUREKA服务器实例

我们先启动profile为eureka1的服务器应用,控制台输入eureka1之后,eureka服务启动,启动好后,eureka1会按照配置主动向另一个eureka示例eureka2注册,但是此时eureka2的服务还没有启动,所以会报一个无法连接的错误。

Spring Cloud微服务开发笔记2——Eureka集群搭建

这个不要紧。我们访问eureka1的服务查看主页:http://slave1:8761/

可以看到我们配置的它会向slave2注册信息,等同于一个服务副本,所以看到DS Replicaas有一个slave2.

但是因为eureka2还没有起来,所以下面的实例列表里面什么都没有。

Spring Cloud微服务开发笔记2——Eureka集群搭建

之后,我们启动eureka2的服务。

Spring Cloud微服务开发笔记2——Eureka集群搭建

我们会发现此次eureka2启动没有报错

并且请求http://slave2:8762/,可以看到eureka2的服务也已经起来,slave1可视为其服务副本。并且重要的是eureka2没报错是因为,eureka1此时已经启动,eureka2成功完成了注册。

Spring Cloud微服务开发笔记2——Eureka集群搭建

我们此时再看http://slave1:8761/,发现eureka1等到eureka2也启动之后,完成既定配置的注册。

Spring Cloud微服务开发笔记2——Eureka集群搭建

细心的你也许发现主页上的实例的application名称都是UNKOWN,这是因为我们没有再application.yml文件中配置spring.application.name的缘故,如果你配置了,那么它会作为实例的Application名称显示在实例列表里。

启动一个服务的提供者的两个实例

下面我们启动客户端服务提供者一个FIRST-SERVICE-APP,端口输入为8101。

Spring Cloud微服务开发笔记2——Eureka集群搭建

我们刷新两个eureka的主页:http://slave1:8761/

Spring Cloud微服务开发笔记2——Eureka集群搭建

还有http://slave2:8762/

Spring Cloud微服务开发笔记2——Eureka集群搭建

之后我们再启动一个FIRST-SERVICE-APP,端口8102

再次刷新两个eureka服务器实例的主页

Spring Cloud微服务开发笔记2——Eureka集群搭建

还有

Spring Cloud微服务开发笔记2——Eureka集群搭建

我们发现,与刚才相比,FIRST-SERVICE-APP的那一行的Status字段多了一个IP地址,一看端口正是8102

启动一个实现了负载均衡请求的服务请求者

然后我们启动一个客户端服务请求者应用,请求其对应的url,其controller做的是向服务提供者FIRST-SERVICE-APP请求http://first-service-app/petinfo/findpet

这个我们着重看info这个属性字段,我们填入的是:服务提供者FIRST-SERVICE-APP的控制器处理接收到的请求时把请求的url保留,填入info字段。从这个字段可以看出这个FIRST-SERVICE-APP服务提供者是那台实例的IP。这个IP来自于服务请求者客户端的直接请求,也就是说服务请求者自己在获取了FIRST-SERVICE-APP的服务列表信息之后,得知了FIRST-SERVICE-APP有两台服务器,它自己借助于配置的Ribbon负载均衡框架自己选了请求那台FIRST-SERVICE-APP服务器。

Spring Cloud微服务开发笔记2——Eureka集群搭建

当我们多次请求,发现其请求的服务器是变化的,实现了负载均衡。

Spring Cloud微服务开发笔记2——Eureka集群搭建

Spring Cloud微服务开发笔记2——Eureka集群搭建

Spring Cloud微服务开发笔记2——Eureka集群搭建

Spring Cloud微服务开发笔记2——Eureka集群搭建

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
Spring Cloud Eureka源代码解析(1)Eureka启动,原生启动与SpringCloudEureka启动异同
Eureka作为服务注册中心对整个微服务架构起着最核心的整合作用,因此对Eureka还是有很大的必要进行深入研究。Eureka1.x版本是纯基于servlet的应用。为了与springcloud结合使用,除了本身eureka代码,还有个粘合模块springcloudnetflixeurekaserver。在我们启动EurekaServer实例
Easter79 Easter79
3年前
SpringCloud系列五:为Eureka Server添加用户认证及元数据
1\.回顾上一篇博客讲解了Eureka集群及将微服务注册到集群上。在前面的讲解中,EurekaServer都是允许匿名访问的,本次将讲解如何构建一个需要登录才能访问的EurekaServer。2\.为EurekaServer添加用户认证复制项目microservicediscoveryeureka,将Art
Stella981 Stella981
3年前
Golang注册Eureka的工具包goeureka发布
1.简介提供Go微服务客户端注册到Eureka中心。点击:github地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2FSimonWang00%2Fgoeureka),欢迎各位多多star!(已通过测试验证,用于正式生产部署)2.原理
Easter79 Easter79
3年前
SpringCloud Eureka服务治理机制
一、基础架构!(https://oscimg.oschina.net/oscnet/c088a917c16ee8be06202e47bd73e50a7a0.png)构建Eureka服务治理有三个核心角色:服务注册中心、服务提供者和服务消费者。上图就是这三个角色之间的通信工作架构图。服务注册中心(Eureka 
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Stella981 Stella981
3年前
Spring cloud实现FeignClient指定Zone调用
本文基于SpringCloudFincheleySR3背景介绍目前项目多个区域多个集群,这些集群共用同一个Eureka集群。通过设置eureka.instance.metadatamap.zone设置不同实例所属的zone,zone之间不互相调用,只有zone内部调用(其实这里用zone做了集群隔离,实际上集群肯定是跨可用区
Stella981 Stella981
3年前
Spring Cloud Eureka的基础架构
基础架构服务注册中心:Eureka提供的服务端,提供服务注册于发现的功能,也就是在上一节中我们实现的eurekaserver服务提供者:提供服务的应用,可以是springBoot应用,也可以是其他技术平台且遵循Eureka通信机制的应用。它将自己提供的服务注册到Eureka,以供其他应用发现,也