SpringCloud系列--(二)服务的注册于发现
在开始SpringCloud的学习的时候,需要了解一下微服务的概念,还要知道SpringBoot的知识。如果不了解的话可以看这篇文章:2小时学会springboot。上一篇是:SpringCloud系列--(一)微服务。
一、简介
SpringCloud为开发人员提供了快速构建分布式系统的一些常见模式工具(例如:配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调导致了样板模式,使SpringCloud的开发人员可以快速支持实现这些模式的服务和应用程序。他们将在任何分布式环境运行良好包括开发人员自己的笔记本电脑,裸机数据中心,以及Cloud Foundry等托管平台。
一堆名词,先解释一波:
1.1 配置管理:当应用变得越来越多大而不得不进行服务化拆分的时候,会发现各种provider实例越来越多。修改某一项配置就会变得越来越麻烦,你常常不得不为修改某一项配置而重启某个服务所有provider实例,甚至为了灰度上线需要更新部分provider的配置。这个时候有一套配置文件集中管理方案就变得十分重要,SpringCloudConfig和SpringCloudBus就是这种问题的解决方案之一,业界也有些知名的同类开源产品,比如百度的disconf。
1.2 服务发现:对于微服务的治理来说,核心部分算得上是注册于发现了。在SpringCloud 中提供了多种服务注册与发现组件:Eureka,Consul,Zookeeper。以往服务间资源的获取都是通过相互调用,比如A获取B相关资源,A调用B提供的API获取相关资源 。加入Eureka之后,则B提供服务资源需要在Eureka服务中心注册一遍,A获取服务资源也需要在Eureka服务中心注册一遍,从而获取B服务的资源。
1.3 断路器: 在微服务结构的分布式系统中,一个系统调用另外一个远程系统是非常普遍的。这种远程调用的被调用方可能是另外一个进程,或者是跨网络的另外一台主机,这种远程的调用和进程的内部调用最大的区别是:远程调用可能会失败,或者挂起没有回应,直到超时。更坏的请情况是,如果多个调用者对同一个挂起的服务进行调用,那么就很有可能的是一个服务的超时等待迅速蔓延到整个分布式系统,引起连锁反应,从而消耗整个分布式系统大量资源。最终可能导致系统瘫痪。
熔断器:就是为了解决这个这种瀑布似的连锁反应导致的灾难。
一旦某个电器出问题,为了防止灾难,电路的保险丝就会熔断。断路器类似于电路的保险丝, 实现思路非常简单,可以将需要保护的远程服务封装起来,在内部监听失败次数,一旦失败次数达到某值时,所有后续对该服务的调用,熔断器截获后就直接返回错误给调用方,而不会继续调用已经出现错误的服务,从而保护调用方的目的, 整个系统也就不会出现因为超时而产生的瀑布式连锁反应。
1,4 智能路由:Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。
在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理(下一篇文章讲述),配置服务的配置文件放在git仓库,方便开发人员随时改配置。
1.5 控制总线
需求问题:如何实现配置文件的实时更新,也就是说,当配置改变的时候,我们如何将整个系统的配置进行更新。当然我们实现的方式可以是通过/refresh接口和Git仓库的web hook来实现Git仓库中的内容修改出发应用程序的属性更新。但是如果所有的出发操作均由我们手工维护 web hook 的话。随着应用的扩张,会变得越来越难维护,而消息中间件就由此而生。
消息中间件:他就可以将将消息路由到一个或者是多个目的地。例如:RabbitMQ.
RabbitMQ是一个消息队列,主要实现应用程序的异步和解耦。同时也能起到消息缓冲,消息分发的作用。它最主要的作用就是解耦,它最标准的用法就是生产者生产消息到队列,消费者从队列中拿出消息并处理,生产者不用管谁来消费,消费者不用关心谁在生产消息。从而达到解耦。在分布式中,消息队列通常起到的一个作用就是分布式事务的支持,RPC调用。
我们来看一个图解。是一个很清楚很明白的一张图。
通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,在这里RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。
二、上代码环节
2.1 环境:
Eclipse+Maven 基于springBoot项目搭建SpringClould工程。
2.2 父项目
(1)建立Maven项目--父 (sc-f-chapter1)
Maven poject -->pom
(2) pom文件,spring Boot版本为2.0.3.RELEASE,Spring Cloud版本为Finchley.RELEASE。
<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"> <modelVersion>4.0.0</modelVersion> <groupId>com.forezp</groupId> <artifactId>sc-f-chapter1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <modules> <module>eureka-server</module> </modules> </project>
2.3 建立服务端 (eureka-server)
(1)创建完成后再pom中引入spring-cloud-starter-netflix-eureka-server的依赖
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.forezp</groupId> <artifactId>sc-f-chapter1</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.forezp</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-server</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </project>
(2)启动一个服务注册中心
只需要一个注解@EnableEurekaServer,这个注解需要在springboot工程的启动application类上加:
package org.eureka.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * <br> * * <pre> *类名:org.eureka.server EurekaServerApplication.java *描述: *基本思路: *public方法: *特别说明: *版权: * 创建时间:2019年2月1日下午3:03:42 *编写者: 修改说明: *<br> * </pre> ******************************************** */ @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
(3)eureka是一个高可用的组件,他没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成),在默认情况下erureka server也是一个eureka client ,它必须指定一个server。eureka的配置文件为application.yml:
server: port: 8761 eureka: instance: hostname: localhost client: registerWithEureka: false #false来表明自己是一个eureka server. fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ spring: application: name: eurka-server
(4)启动工程,打开浏览器 http://lcoalhost:8761.界面如下。
2.4 创建一个Client
当client向server注册时,它会提供一些元数据,例如主机,端口、url、主页等。Eureka server从每个Client接收心跳消息。如果消息超时,通常将该实例从server中删除。
(1)pom文件
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.forezp</groupId> <artifactId>sc-f-chapter1</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.forezp</groupId> <artifactId>service-hi</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-hi</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <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> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
(2)通过注解@EnableEurekaClient 表明自己是一个eurekaclient.
package org.service.hi; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** *<br><pre> *类名:org.service.hi ServiceHiApplication.java *描述: *基本思路: *public方法: *特别说明: *版权: * 创建时间:2019年2月1日下午4:37:06 *编写者: 修改说明: *<br></pre> ******************************************** */ @SpringBootApplication @EnableEurekaClient @RestController public class ServiceHiApplication { public static void main(String[] args) { SpringApplication.run( ServiceHiApplication.class, args ); } @Value("${server.port}") String port; @RequestMapping("/hi") public String home(@RequestParam(value = "name", defaultValue = "forezp") String name) { return "hi " + name + " ,i am from port:" + port; } }
(3)在配置文件中注明自己的服务注册中心的地址,application.yml配置文件如下:
server: port: 8762 spring: application: name: service-hi eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
(4)指明spring: application: name: service-hi在以后的服务与服务之间相互调用一般都是根据这个name 。
启动工程,打开http://localhost:8761 ,即eureka server 的网址:
(5)这个时候也可以打开另一个网址 name值可以自己定以后传值,不定义的话,就默认后台数据