微服务架构与springcloud04——Eureka服务注册与发现

矫情吗;* 2023-09-30 09:01 77阅读 0赞
4.1 Eureka的基础知识

如果你有自己的私人医生,那么你需要时直接与医生进行联系就可以。但大多数人都需要去医院,医院有很多病人,也有很多医生,那么就需要一个窗口来挂号、取号、管理余号等等。同样的道理,当我们的服务数量变得多起来,就需要进行服务注册与发现的管理了。

image-20220311213144195

注:心跳连接就是指像心跳一样周期性的监测服务是否可用。

它与服务提供者、消费者的关系如下图。

image-20220311212913873

server与client的说明如下。image-20220311213702018

4.2 Eureka服务端安装

(1)建cloud-eureka-server7001模块

(2)写pom

  1. <dependencies>
  2. <!-- eureka-server -->
  3. <dependency>
  4. <groupId>org.springframework.cloud</groupId>
  5. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  6. </dependency>
  7. <!--引入自定义的api通用包,可以使用Payment支付bean-->
  8. <dependency>
  9. <groupId>com.wangzhou.springcloud</groupId>
  10. <artifactId>cloud-api-commons</artifactId>
  11. <version>${project.version}</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-web</artifactId>
  16. </dependency>
  17. <!--监控-->
  18. <dependency>
  19. <groupId>org.springframework.boot</groupId>
  20. <artifactId>spring-boot-starter-actuator</artifactId>
  21. </dependency>
  22. <!-- 一般通用配置 -->
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-devtools</artifactId>
  26. <scope>runtime</scope>
  27. <optional>true</optional>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.projectlombok</groupId>
  31. <artifactId>lombok</artifactId>
  32. <optional>true</optional>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-test</artifactId>
  37. <scope>test</scope>
  38. </dependency>
  39. </dependencies>

(3)写yml

application.yml

  1. server:
  2. port: 7001
  3. eureka:
  4. instance:
  5. hostname: localhost #eureka服务端的实例名称
  6. client:
  7. #false表示不向注册中心注册自己(想注册也可以,不过没必要)
  8. register-with-eureka: false
  9. #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
  10. fetch-registry: false
  11. service-url:
  12. #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
  13. defaultZone: http://${
  14. eureka.instance.hostname}:${
  15. server.port}/eureka/

(4)主启动

  1. @SpringBootApplication
  2. @EnableEurekaServer
  3. public class EurekaMain7001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaMain7001.class ,args);
  6. }
  7. }

(5)测试

启动模块,访问Eureka,可以看到如下界面。

image-20220311220320590

4.3 支付微服务入驻

我们要把服务的提供者:支付微服务入驻到EurekaServer。这很像培训机构(如尚硅谷)向物业公司进行注册。

(1)引入依赖

  1. <!-- eureka-client -->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>

(2)标注解

主启动类标注解@EnableEurekaClient

  1. @EnableEurekaClient
  2. @SpringBootApplication
  3. public class PaymentMain8001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(PaymentMain8001.class, args);
  6. }
  7. }

(3)改yaml

  1. eureka:
  2. client:
  3. #true表示向注册中心注册自己,默认为true
  4. register-with-eureka: true
  5. #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
  6. fetch-registry: true
  7. service-url:
  8. defaultZone: http://localhost:7001/eureka

访问:Eureka,可以看到8001服务已经入驻。

image-20220314201650570

4.4 订单微服务入驻

服务的消费者:订单微服务也需要入驻到Eureka.

(1)改pom

  1. <!-- eureka-client -->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>

(2)改yaml

  1. spring:
  2. application:
  3. name: cloud-order-service
  4. eureka:
  5. client:
  6. register-with-eureka: true
  7. fetch-registry: true
  8. service-url:
  9. defaultZone: http://localhost:7001/eureka

(3)主启动

  1. @EnableEurekaClient
  2. @SpringBootApplication
  3. public class OrderMain80 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(OrderMain80.class, args);
  6. }
  7. }

(4)测试

image-20220314203129564

4.5 Eureka集群
4.5.1 集群原理

Eureka工作流程可以参考下图。

image-20220314204803206

微服务RPC的核心就是高可用,如果某个Eureka服务节点出现故障,不能够影响整体的服务环境。我们可以搭建Euraka注册中心集群,实现负载均衡和故障容错。参考下图,7001和7002互相注册,然后对外暴露出一个整体。集群主机数量可以随着业务量而定。

image-20220314205408320

4.5.2 搭建集群

(1)建module

cloud-eureka-server7002

(2)写pom

直接cv7001

  1. <dependencies>
  2. <!-- eureka-server -->
  3. <dependency>
  4. <groupId>org.springframework.cloud</groupId>
  5. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  6. </dependency>
  7. <!--引入自定义的api通用包,可以使用Payment支付bean-->
  8. <dependency>
  9. <groupId>com.wangzhou.springcloud</groupId>
  10. <artifactId>cloud-api-commons</artifactId>
  11. <version>${project.version}</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-web</artifactId>
  16. </dependency>
  17. <!--监控-->
  18. <dependency>
  19. <groupId>org.springframework.boot</groupId>
  20. <artifactId>spring-boot-starter-actuator</artifactId>
  21. </dependency>
  22. <!-- 一般通用配置 -->
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-devtools</artifactId>
  26. <scope>runtime</scope>
  27. <optional>true</optional>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.projectlombok</groupId>
  31. <artifactId>lombok</artifactId>
  32. <optional>true</optional>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-test</artifactId>
  37. <scope>test</scope>
  38. </dependency>
  39. </dependencies>

(3)改yaml

是不是也可以直接cv7001呢?

  1. server:
  2. port: 7002
  3. eureka:
  4. instance:
  5. hostname: localhost #eureka服务端的实例名称
  6. client:
  7. #false表示不向注册中心注册自己(想注册也可以,不过没必要)
  8. register-with-eureka: false
  9. #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
  10. fetch-registry: false
  11. service-url:
  12. #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
  13. defaultZone: http://${
  14. eureka.instance.hostname}:${
  15. server.port}/eureka/

读者看看看7001与7002配置文件hostname完全相同,无法进行区分。

由于我们现在只有一个主机,我们可以修改C:\Windows\System32\drivers\etc\hosts实现将1个ip地址映射为多个网址。

  1. 127.0.0.1 eureka7001.com
  2. 127.0.0.1 eureka7002.com

现在修改两个模块的yaml文件的hostname,并且让他们相互注册。

7001

  1. server:
  2. port: 7001
  3. eureka:
  4. instance:
  5. hostname: eureka7001.com #eureka服务端的实例名称
  6. client:
  7. #false表示不向注册中心注册自己(想注册也可以,不过没必要)
  8. register-with-eureka: false
  9. #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
  10. fetch-registry: false
  11. service-url:
  12. #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
  13. defaultZone: http://eureka7002.com:7002/eureka/

7002

  1. server:
  2. port: 7002
  3. eureka:
  4. instance:
  5. hostname: eureka7002.com #eureka服务端的实例名称
  6. client:
  7. #false表示不向注册中心注册自己(想注册也可以,不过没必要)
  8. register-with-eureka: false
  9. #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
  10. fetch-registry: false
  11. service-url:
  12. #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
  13. defaultZone: http://eureka7001.com:7001/eureka/

(4)主启动

  1. @EnableEurekaServer
  2. @SpringBootApplication
  3. public class EurekaMain7002 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaMain7002.class ,args);
  6. }
  7. }

(5)测试

访问http://eureka7001.com:7001/,http://eureka7002.com:7002/

image-20220314213410173

4.5.3 订单、支付服务发布到集群

将80,8001模块yaml文件修改如下。

image-20220314213925325

测试,重启各个服务,注意先启动EurekaServer(7001/7002),再启动服务提供模块(8001),再启动服务消费模块(80)。

测试结果如下。

image-20220314214855505

image-20220314214835606

#
4.6 支付服务集群

现在Eureka服务注册中心已经是集群服务了,我们还要把支付服务也变成支付集群服务。

(1)建module

新建cloud-provider-payment8002

(2)改pom

  1. <dependencies>
  2. <!-- 这两个依赖一般绑定在一起使用 -->
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter-web</artifactId>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-actuator</artifactId>
  10. </dependency>
  11. <!-- 整合mybatis与springboot -->
  12. <dependency>
  13. <groupId>org.mybatis.spring.boot</groupId>
  14. <artifactId>mybatis-spring-boot-starter</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>com.alibaba</groupId>
  18. <artifactId>druid-spring-boot-starter</artifactId>
  19. <!--子工程写了版本号,就使用子工程的版本号,如果没写版本,找父工程中规定的版本号-->
  20. <version>1.1.10</version>
  21. </dependency>
  22. <!--mysql-connector-java-->
  23. <dependency>
  24. <groupId>mysql</groupId>
  25. <artifactId>mysql-connector-java</artifactId>
  26. </dependency>
  27. <!--jdbc-->
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-jdbc</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-devtools</artifactId>
  35. <scope>runtime</scope>
  36. <optional>true</optional>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.projectlombok</groupId>
  40. <artifactId>lombok</artifactId>
  41. <optional>true</optional>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.springframework.boot</groupId>
  45. <artifactId>spring-boot-starter-test</artifactId>
  46. <scope>test</scope>
  47. </dependency>
  48. <!--引入自定义的api通用包,可以使用Payment支付bean-->
  49. <dependency>
  50. <groupId>com.wangzhou.springcloud</groupId>
  51. <artifactId>cloud-api-commons</artifactId>
  52. <version>${project.version}</version>
  53. </dependency>
  54. <!-- eureka-client -->
  55. <dependency>
  56. <groupId>org.springframework.cloud</groupId>
  57. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  58. </dependency>
  59. </dependencies>

(3)写yaml

  1. # 服务端口号
  2. server:
  3. port: 8002
  4. # 服务名称
  5. spring:
  6. application:
  7. name: cloud-payment-service
  8. #数据库配置
  9. datasource:
  10. type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
  11. driver-class-name: com.mysql.jdbc.Driver # mysql驱动包
  12. url: jdbc:mysql://localhost:3306/cloud2021?useUnicode=true&characterEncoding=UTF-8&useSSL=false
  13. username: "root"
  14. password: "123456"
  15. eureka:
  16. client:
  17. #true表示向注册中心注册自己,默认为true
  18. register-with-eureka: true
  19. #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
  20. fetch-registry: true
  21. service-url:
  22. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  23. mybatis:
  24. mapper-locations: classpath:mapper/*.xml
  25. type-aliases-package: com.wangzhou.springcloud.entities # 所有Bean 别名类所在包

(4)主启动与业务类

直接在存储目录进行cv(如下图),注意,不要在Idea中进行拷贝,可能会爆红。把主启动类的名字改为PaymentMain8002

image-20220316202457826

8001,8002模块对于外部暴露的服务名称是一样的,如何区分是哪个服务模块进行请求的响应呢?

image-20220316203640121

可以在两个模块的controller中增加serverPort属性,在请求方法中使用。

  1. @Value("${server.port}")
  2. private String serverPort;
  3. @PostMapping("/create")
  4. public CommonResult create(@RequestBody Payment payment) {
  5. int result = service.create(payment);
  6. log.info("插入结果是:" + result);
  7. if(result > 0) {
  8. return new CommonResult(200, "插入数据成功, serverPort:" + serverPort, result);
  9. } else {
  10. return new CommonResult(444, "插入数据失败");
  11. }
  12. }
  13. @GetMapping(value = "/get/{id}")
  14. public CommonResult getPaymentById(@PathVariable("id")Long id){
  15. Payment result = service.getPaymentById(id);
  16. log.info("-----查询结果----: "+ result);
  17. if (result != null){
  18. //查询成功
  19. return new CommonResult(200,"查询成功, serverPort:" + serverPort,result);
  20. }else {
  21. return new CommonResult(444,"没有对应记录,查询id: "+id,null);
  22. }
  23. }

serverPort其实是从yml文件中读取的。

image-20220316203248942

按顺序(Eureka服务7001,7002、服务提供模块8001,8002、服务消费模块80,后面不再赘述)启动各个服务模块。

(5)测试

image-20220316205859316

image-20220316210105037

看上去没有问题,不过您如果多尝试发几次上面的请求,会发现每次使用的端口都是8001.这是因为我们订单服务把访问的url写死了。

image-20220316210339922

可以将其改为支付微服务统一暴露的服务名称.

image-20220316210656719

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

改完以后测试,发现程序奔溃。

image-20220316211007801

这是因为我们使用支付微服务统一暴露的名称CLOUD-PAYMENT-SERVICE进行访问,但是我们程序并不知道需要8001还是8002响应服务.也就是说很多老师在一起办了学校,现在学校办好了,可以统一使用学校的招牌招生了,但具体哪个老师教哪个学生的分配机制我们还没有确立。

现在我们来实现这个机制:负载均衡机制。在OrderPayment模块的ApplicationContextConfig类中添加注解@LoadBalanced

image-20220316211528448

添加后测试发现8001端口测试跑通,8002响应时还是返回异常页面。可以仔细比对两个模块,发现原来少拷贝了内容、将8001中的PaymentMapper.xml拷贝到8002模块,重新启动服务即可。

4.7 actutor微服务完善

不知您是否发现下图暴露了我们电脑的主机名称。我们认为,我们暴露主机名称毫无意义,而且我们希望将ip地址暴露出去。

image-20220316214032475

在8001,8002,80模块的配置类中增加instance属性即可。

image-20220316220114677

image-20220316215406602

当鼠标放到8001上去,左下角出现了ip地址。

image-20220316220036843

4.8 服务发现

现在Eureka就像一个大的中介公司,里面注册许多服务,比如payment服务集群8001,8002。不过目前为止我们的服务对于其它服务的细节并不清楚,我们有必要提供一个机制把相关信息暴露出去,为此Eureka提供了服务发现机制。这很类似于我们在租房中介公司看各个房源的信息。

在8001模块的PaymentController中增加如下代码。

  1. @Resource
  2. private DiscoveryClient discoveryClient;
  3. @GetMapping("/discovery")
  4. public Object Discovery() {
  5. List<String> services = discoveryClient.getServices();
  6. for (String service : services) {
  7. log.info("****service:****" + service);
  8. }
  9. List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
  10. for (ServiceInstance instance : instances) {
  11. log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" +instance.getUri());
  12. }
  13. return this.discoveryClient;
  14. }

注:import org.springframework.cloud.client.discovery.DiscoveryClient;导包不要导错。

在主启动类中增加注解@EnableDiscoveryClient

访问localhost:8001/payment/discovery

image-20220323203245413

idea后台可以看到。

image-20220323203418662

事实上,我们可以让80Order端口获得payment服务的信息。这更加贴近实际的需求场景。

4.9 自我保护机制

细心的同学可能发现,访问Eureka (eureka7002.com),会有如下提醒.

image-20220323204715936

如果出现上面的提醒,说明Eureka进入了保护模式,这说明即使某一刻一个微服务变得不可用了,因为可能因为网络延时,卡顿等各种原因没有收到微服务的心跳连接,Eureka也不会立刻清理该服务,这是Eureka对于该服务存在重新恢复可能性的一种现场保护机制,比如疫情期间某商铺无法缴纳租金费,房东可能可以允许其拖欠几天,而不是立刻清理出户.

在7001模块中可以添加如下内容关闭自我保护机制.

image-20220323211507950

在8001模块配置如下

  1. eureka:
  2. instance:
  3. #Eureka客户端向服务端发送心跳的时间间隔,单位秒(默认30秒)
  4. lease-renewal-interval-in-seconds: 1
  5. #Eureka服务端在收到最后一次心跳后等待的时间上限,单位秒(默认90秒),超时剔除服务
  6. lease-expiration-duration-in-seconds: 2

访问Eureka (eureka7001.com),提示自我保护机制关闭.

image-20220323210509531

停掉8001服务.可以看到果然服务立刻被清理了.

image-20220323211119889

发表评论

表情:
评论列表 (有 0 条评论,77人围观)

还没有评论,来说两句吧...

相关阅读