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

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

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

1server: 2 port: 8761 3eureka: 4 client: 5 register-with-eureka: false 6 fetch-registry: false

现在我们改成:

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

1server: 2 port: 8761 3spring: 4 profiles: eureka1 5eureka: 6 client: 7 service-url: 8 defaultZone: http://slave2:8762/eureka 9--- 10server: 11 port: 8762 12spring: 13 profiles: eureka2 14eureka: 15 client: 16 service-url: 17 defaultZone: http://slave1:8761/eureka 18 19

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

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

1package com.happybks.demo; 2 3import java.util.Scanner; 4 5import org.springframework.boot.autoconfigure.SpringBootApplication; 6import org.springframework.boot.builder.SpringApplicationBuilder; 7import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 8 9@SpringBootApplication 10@EnableEurekaServer 11public class EurekaServerApplication { 12 13 public static void main(String[] args) { 14 Scanner sc=new Scanner(System.in); 15 final String profile = sc.nextLine(); 16 new SpringApplicationBuilder(EurekaServerApplication.class).profiles(profile).run(args); 17 sc.close(); 18 } 19}

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

application.yml配置文件

1spring: 2 application: 3 name: first-service-app 4eureka: 5 client: 6 service-url: 7 defaultZone: http://slave1:8761/eureka/,http://slave2:8761/eureka/

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

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

PetBean增加一个info字段。

1package com.happybks.beans; 2 3public class PetBean { 4 private String breedType; 5 private int age; 6 private double price; 7 private String info; 8 public String getBreedType() { 9 return breedType; 10 } 11 public void setBreedType(String breedType) { 12 this.breedType = breedType; 13 } 14 public int getAge() { 15 return age; 16 } 17 public void setAge(int age) { 18 this.age = age; 19 } 20 public double getPrice() { 21 return price; 22 } 23 public void setPrice(double price) { 24 this.price = price; 25 } 26 public String getInfo() { 27 return info; 28 } 29 public void setInfo(String info) { 30 this.info = info; 31 } 32 public PetBean() { 33 34 } 35 @Override 36 public String toString() { 37 return "PetBean [breedType=" + breedType + ", age=" + age + ", price=" + price + ", info=" + info + "]"; 38 } 39 40}

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

1package com.happybks.controllers; 2 3import javax.servlet.http.HttpServletRequest; 4 5import org.springframework.http.MediaType; 6import org.springframework.web.bind.annotation.GetMapping; 7import org.springframework.web.bind.annotation.RestController; 8 9import com.happybks.beans.PetBean; 10 11@RestController 12public class PetInfoController { 13 14 @GetMapping(value="/petinfo/findpet",produces=MediaType.APPLICATION_JSON_VALUE) 15 public PetBean findPet(HttpServletRequest request) { 16 PetBean petBean = new PetBean(); 17 petBean.setBreedType("聪明机灵小柴犬"); 18 petBean.setAge(1); 19 petBean.setPrice(50000); 20 petBean.setInfo(request.getRequestURL().toString()); 21 return petBean; 22 } 23 24}

启动类:

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

1package com.happybks.providers; 2 3import java.util.Scanner; 4 5import org.springframework.boot.autoconfigure.SpringBootApplication; 6import org.springframework.boot.builder.SpringApplicationBuilder; 7import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8import org.springframework.context.annotation.ComponentScan; 9 10 11@SpringBootApplication 12@EnableEurekaClient 13@ComponentScan(basePackages = { "com.happybks.controllers" }) 14public class ServiceProvider1Application { 15 16 public static void main(String[] args) { 17 18 Scanner sc=new Scanner(System.in); 19 String port=sc.nextLine(); 20 new SpringApplicationBuilder(ServiceProvider1Application.class).properties("server.port="+port).run(args); 21 sc.close(); 22 } 23}

配置服务调用者

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

1package com.happybks.controllers; 2 3import javax.servlet.http.HttpServletRequest; 4 5import org.springframework.cloud.client.loadbalancer.LoadBalanced; 6import org.springframework.context.annotation.Bean; 7import org.springframework.context.annotation.Configuration; 8import org.springframework.web.bind.annotation.GetMapping; 9import org.springframework.web.bind.annotation.PathVariable; 10import org.springframework.web.bind.annotation.RestController; 11import org.springframework.web.client.RestTemplate; 12 13@RestController 14@Configuration 15public class ShopperBehaviorController { 16 17 @Bean 18 @LoadBalanced 19 public RestTemplate getRestTemplate() { 20 return new RestTemplate(); 21 } 22 23 @GetMapping("/shopper/routine") 24 public String routine() { 25 RestTemplate restTemplate = getRestTemplate(); 26 String json = restTemplate.getForObject("http://first-service-app/petinfo/findpet", String.class); 27 return json; 28 } 29 30 @GetMapping("/shopper/getservice/{id}") 31 public String getservice(@PathVariable("id") Integer id, HttpServletRequest request) { 32 RestTemplate restTemplate = getRestTemplate(); 33 String json = restTemplate.getForObject("http://first-service-app/petinfo/findpet", String.class); 34 System.out.println(request.getRequestURL().toString()); 35 return json; 36 } 37}

启动类:

1package com.happybks.callers; 2 3import org.springframework.boot.SpringApplication; 4import org.springframework.boot.autoconfigure.SpringBootApplication; 5import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6import org.springframework.context.annotation.ComponentScan; 7 8@SpringBootApplication 9@ComponentScan(basePackages = { "com.happybks.controllers" }) 10@EnableEurekaClient 11public class ServiceCaller1Application { 12 13 public static void main(String[] args) { 14 SpringApplication.run(ServiceCaller1Application.class, args); 15 } 16}

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

启动集群

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

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

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

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

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

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

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

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

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

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

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

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

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

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

还有http://slave2:8762/

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

再次刷新两个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服务器。

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

点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

SpringCloud系列五:为Eureka Server添加用户认证及元数据

1\.回顾上一篇博客讲解了Eureka集群及将微服务注册到集群上。在前面的讲解中,EurekaServer都是允许匿名访问的,本次将讲解如何构建一个需要登录才能访问的EurekaServer。2\.为EurekaServer添加用户认证复制项目microservicediscoveryeureka,将Art

Golang注册Eureka的工具包goeureka发布

1.简介提供Go微服务客户端注册到Eureka中心。点击:github地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2FSimonWang00%2Fgoeureka),欢迎各位多多star!(已通过测试验证,用于正式生产部署)2.原理