注:一些内容是个人见解,如有不准确的欢迎指正~
一,基本概念
SpringCloud定义
Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调导致了样板模式, 使用Spring Cloud开发人员可以快速地支持实现这些模式的服务和应用程序。他们将在任何分布式环境中运行良好,包括开发人员自己的笔记本电脑,裸机数据中心,以及Cloud Foundry等托管平台。
特性
Spring Cloud专注于提供良好的开箱即用经验的典型用例和可扩展性机制覆盖。
- 分布式/版本化配置
- 服务注册和发现
- 路由
- service - to - service调用
- 负载均衡
- 断路器
- 分布式消息传递
Eureka的基础架构
Eureka的基础架构:
服务提供
服务消费者
二,简单模块(配合代码Demo演示)
eureka server
一、服务发现组件我们为什么选择Eureka?
1.Eureka来自生产环境,这是它天生的优势
2.Spring Cloud对Eureka支持很好
二、Eureka简介
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。
SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现Spring Cloud的服务发现功能。
Eureka项目相当活跃,代码更新相当频繁,目前最新版本是1.9.*
2.0的版本也正在紧锣密鼓的开发中,2.0将会带来更好的扩展性,并且使用细粒度的订阅模型取代了基于拉取的模型。
注:(Eureka 2.0 开源工作宣告停止,继续使用风险自负) 尴尬~
三、Eureka Server 本身也支持集群。(跨区域等)
四 、代码实例
1.maven配置
<!-- springBoot的jar -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/>
</parent>
<!-- eureka-server -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- springCloud的基础jar -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.启动方法
@EnableEurekaServer //启动一个服务注册中心提供给其他应用进行对话
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.配置参数
#服务名称
spring.application.name=eureka-server
#端口号
server.port=1001
#设为false,关闭自我保护主要(默认是关闭的)
eureka.server.enable-self-preservationz = true
#清理间隔(单位毫秒,默认是60*1000)
eureka.server.eviction-interval-timer-in-ms = 60000
eureka.instance.hostname=localhost
#是否向服务注册中心注册自己
# (在默认配置下,Eureka Server会将自己也作为客户端来尝试注册自己,我们需要禁用它的客户端禁用行为)
eureka.client.register-with-eureka=false
#是否检索服务
eureka.client.fetch-registry=false
#服务注册中心的配置内容,指定服务注册中心的位置 高可用的注册(不同地域,启动不同的eureka池)
# eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
4.启动流程
@EnableEurekaServer包含了所有的Eureka Server启动配置。
但是在EnableEurekaServer的注解声明中,没有看到任何初始化bean定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
但是在EurekaServerMarkerConfiguration的配置中,定义了一个空的类并进行了实例化。按照这个类的探索,发现其实以上都是一个开关,真正的配置都在EurekaServerAutoConfiguration中实现。
@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
}
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
public class EurekaClientAutoConfiguration {
}
总结来说,过程是:配置@EnableEurekaServer-@Import-》EurekaServerMarkerConfiguration-创建Marker实例-》扫描到EurekaServerAutoConfiguration时,@ConditionalOnBean满足加载条件,进行加载。
Eureka的启动接口为com.netflix.eureka.EurekaBootStrap。EurekaBootStrap实现了javax.servlet.ServletContextListener,并在contextInitialized函数中初始化Eureka的参数和服务启动。
/**
* Initializes Eureka, including syncing up with other Eureka peers and publishing the registry.
*
* @see
* javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
*/
@Override
public void contextInitialized(ServletContextEvent event) {
try {
initEurekaEnvironment();//配置信息
initEurekaServerContext();//服务启动
ServletContext sc = event.getServletContext();
sc.setAttribute(EurekaServerContext.class.getName(), serverContext);
} catch (Throwable e) {
logger.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}
Eureka client
微服务中的每一个小的应用(模块);我们创建提供服务的客户端,并向服务注册中心注册自己
首先,创建一个基本的Spring Boot应用。命名为eureka-client
在pom.xml中,加入如下配置:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
main启动配置
@EnableDiscoveryClient //注解用来将当前应用加入到服务治理体系中。
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
//java spi test
/*ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class);
Iterator<Log> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
Log log = iterator.next();
log.execute("iiiiiiiiiiii");
}*/
}
}
application.properties 参数配置
spring.application.name=eureka-client
server.port=2001
#指定服务注册中心地址,类型为 HashMap,并设置有一组默认值,默认的Key为 defaultZone;默认的Value为 http://localhost:8761/eureka ,如果服务注册中心为高可用集群时,多个注册中心地址以逗号分隔。
#如果服务注册中心加入了安全验证,这里配置的地址格式为: http://<username>:<password>@localhost:8761/eureka 其中 <username> 为安全校验的用户名;<password> 为该用户的密码
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
ps:后面启动的服务都与这个类似,只是使用不同的功能就添加不同的pom引入
服务消费者(基础)
服务之间通过接口调用获取数据
(见代码展示)
服务消费者(Ribbon)
定义:
- Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。它可以通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。
- 当Ribbon与Eureka联合使用时,ribbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心中获取服务实例列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。
- 而当Ribbon与Consul联合使用时,ribbonServerList会被ConsulServerList来扩展成从Consul获取服务实例列表。同时由ConsulPing来作为IPing接口的实现。
- 我们在使用Spring Cloud Ribbon的时候,不论是与Eureka还是Consul结合,都会在引入Spring Cloud Eureka或Spring Cloud Consul依赖的时候通过自动化配置来加载上述所说的配置内容,所以我们可以快速在Spring Cloud中实现服务间调用的负载均衡。
使用的通讯协议:
通信协议是HTTP或者HTTPS
使用方式
为RestTemplate增加@LoadBalanced注解
相关实例--
Spring Cloud Feign
Feign 简介
在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client, Spring的RestTemplate。但是,用起来最方便、最优雅的还是要属Feign了。
定义:
- Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
- 它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。
- Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现
- 可以传输文件
比如:
/**
* @ProjectName springcloudroot
* @PackageName com.example.consumer.Controller
* @Author tanjianglong
* @CreatedTime 2017/8/22.
* @Description : eureka-consumer-feign demo
* 修改记录:
* 1:直接调用 http
* 2:提供熔断 降级服务
*/
@RestController
public class DcController {
@Autowired
private DcClient dcClient; //调用服务接口
@GetMapping("/consumer")
public String dc() {
return dcClient.consumer();
}
}
DcClient 代码
@FeignClient(value = "eureka-client",fallback = DcClientImpl.class )
public interface DcClient {
@GetMapping("/dc")
public String consumer();
}
- A: @FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入。
- B: @RequestMapping表示在调用该方法时需要向/dc发送GET请求。
开发者通过dcClient.consumer()就能完成发送HTTP请求和解码HTTP返回结果并封装成对象的过程。
通讯协议:
- Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection 。我们可以用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient依赖:
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>
与dubbo的通讯协议区别-
服务容错保护(Hystrix服务降级 && 断路由)
熔断器
定义
熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
熔断器的作用就是即时发现运行异常的服务, 告知调用者不再调用该服务接口, 从而避免调用者服务资源消耗殆尽。 原理如官网插图:
配置使用
SpringCloud框架内微服务间交互使用Rest或Feign:
maven依赖
<!-- 服务降级,断路由相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
restTemplate 中使用方式
/**
* @author 0217319
* @version V1.0
* @Description: 调用接口类
* @date 2018.8.22
*/
@Service
public class ConsumerService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallback")//服务异常后执行的方法
public String consumer() {
return restTemplate.getForObject("http://eureka-client/dc", String.class);
}
public String fallback() {
return "fallback is execute";
}
}
Feign + Hystrix 使用方式:
OpenFeign是自带断路器的, 不用在pom.xml和入口类添加新增引用, 但hytrix默认是关闭的, 需要在配置文件中加入这个即可
feign.hystrix.enabled=true。
代码使用(首先会调用 eureka-client 服务的 /DC接口 如果发生短融 ,则会调用 DcClientImpl 类的consumer方法)
@FeignClient(value = "eureka-client",fallback = DcClientImpl.class )
public interface DcClient {
@GetMapping("/dc")
String consumer();
}
public class DcClientImpl implements DcClient {
@Override
public String consumer() {
return "error -----";
}
}
依赖隔离
定义
Hystrix则使用该模式实现线程池的隔离,它会为每一个Hystrix命令创建一个独立的线程池,这样就算某个在Hystrix命令包装下的依赖服务出现延迟过高的情况,也只是对该依赖服务的调用产生影响,而不会拖慢其他的服务。
- 通过对依赖服务的线程池隔离实现,可以带来如下优势:
- 应用自身得到完全的保护,不会受不可控的依赖服务影响。即便给依赖服务分配的线程池被填满,也不会影响应用自身的额其余部分。
- 可以有效的降低接入新服务的风险。如果新服务接入后运行不稳定或存在问题,完全不会影响到应用其他的请求。
- 当依赖的服务从失效恢复正常后,它的线程池会被清理并且能够马上恢复健康的服务,相比之下容器级别的清理恢复速度要慢得多。
- 当依赖的服务出现配置错误的时候,线程池会快速的反应出此问题(通过失败次数、延迟、超时、拒绝等指标的增加情况)。同时,我们可以在不影响应用功能的情况下通过实时的动态属性刷新(后续会通过Spring Cloud Config与Spring Cloud Bus的联合使用来介绍)来处理它。
- 当依赖的服务因实现机制调整等原因造成其性能出现很大变化的时候,此时线程池的监控指标信息会反映出这样的变化。同时,我们也可以通过实时动态刷新自身应用对依赖服务的阈值进行调整以适应依赖方的改变。
- 除了上面通过线程池隔离服务发挥的优点之外,每个专有线程池都提供了内置的并发实现,可以利用它为同步的依赖服务构建异步的访问。
断路由
定义
“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),直接切断原来的主逻辑调用。但是,在Hystrix中的断路器除了切断主逻辑的功能之外,还有更复杂的逻辑。还有自我修复功能`
熔断器
- 服务的健康状况 = 请求失败数 / 请求总数. 熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的.
- 当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.
- 当熔断器开关打开时, 请求被禁止通过.
- 当熔断器开关处于打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.
- 熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待. 并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能.
Hystrix的内部处理逻辑
- 构建Hystrix的Command对象, 调用执行方法.
- Hystrix检查当前服务的熔断器开关是否开启, 若开启, 则执行降级服务getFallback方法.
- 若熔断器开关关闭, 则Hystrix检查当前服务的线程池是否能接收新的请求, 若超过线程池已满, 则执行降级服务getFallback方法.
- 若线程池接受请求, 则Hystrix开始执行服务调用具体逻辑run方法.
- 若服务执行失败, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
熔断器DEMO-代码
Hystrix监控面板
定义
断路器是根据一段时间窗内的请求情况来判断并操作断路器的打开和关闭状态的。而这些请求情况的指标信息都是HystrixCommand和HystrixObservableCommand实例在执行过程中记录的重要度量信息,它们除了Hystrix断路器实现中使用之外,对于系统运维也有非常大的帮助。这些指标信息会以“滚动时间窗”与“桶”结合的方式进行汇总,并在内存中驻留一段时间,以供内部或外部进行查询使用,Hystrix Dashboard就是这些指标内容的消费者之一。
使用
maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
代码启动
为应用主类加上@EnableHystrixDashboard,启用Hystrix Dashboard功能。
效果--启动查看
http://localhost:3100/hystrix.stream
三,SpringCloud 和 Dubbo 对比
通讯协议
- Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议,但是Dubbo官网是推荐我们使用Dubbo协议的。
缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。
连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用
其他链接(http,webservice,rmi协议,hessian协议)
连接个数:多连接
连接方式:短连接
传输协议:TCP || HTTP
传输方式:同步传输
序列化:***
不支持文件传输
- SpringCloud 默认 使用http json传输
Spring Cloud也并不是和http+JSON强制绑定的,如有必要Thrift、protobuf等高效的RPC、序列化协议同样可以作为替代方案
差异:
dubbo由于是二进制的传输,占用带宽会更少
springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大
dubbo大多数情况下都是使用长连接小数据量的模式提供服务使用的。所以,对于类似于电商等同步调用场景多并且能支撑搭建Dubbo 这套比较复杂环境的成本的产品而言,Dubbo 确实是一个可以考虑的选择。但如果产品业务中由于后台业务逻辑复杂、时间长而导致异步逻辑比较多的话,可能Dubbo 并不合适。
平台架构
- dubbo框架只是专注于服务之间的治理,如果我们需要使用配置中心、分布式跟踪这些内容都需要自己去集成,这样无形中使用dubbo的难度就会增加。
- Spring Cloud几乎考虑了服务治理的方方面面,更有Spring Boot这个大将的支持,开发起来非常的便利和简单。
社区维护性:
- Dubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于中国各互联网公司.但是没有专门的团队在维护更新了,需要使用者自己就必须要组建一个维护团队,先不论你要准备要集成多少功能做多少改造。包括自己想集成的东西,和现有版本的坑~
- Spring Cloud是大名鼎鼎的Spring家族的产品。Spring专注于企业级开源框架的研发,不论是在中国还是在世界上使用都非常广泛.自从发展到现在,仍然在不断的高速发展,版本和功能将会更加完善,我们可以重持续升级的版本中获益~
四,总结
寻址服务
前端使用服务名而非ip地址调用接口
SpringCloud 带来的不便和方便
开发成本,业务划分,
监控服务
SQL慢查询,节点机内存,Cpu使用情况,线程数,接口响应时间统计
QBS
部分接口做单位时间内最大访问数限制
五,参考资料
个人觉得比较好的基础教程: