只有光头才能变强
认识我的朋友可能都知道我这阵子去实习啦,去的公司说是用springcloud(但我觉得使用的力度并不大啊~~)...
所以,这篇主要来讲讲springcloud的一些基础的知识。(我就是现学现卖了,主要当做我学习springcloud的笔记吧!)当然了,我的水平是有限的,可能会有一些理解错的的概念/知识点,还请大家不吝在评论区指正啊~~
springcloud github demo(看完文章的同学可以自己练手玩玩):
项目结构图:
像我这种技术小白,看到这些词(集群/分布式/微服务/soa
)的时候,感觉就是遥不可及的(高大尚的技术!!)。就好像刚学java面向对象的时候,在论坛上翻阅资料的时候,无意看到"面向切面编程",也认为这是遥不可及的(高大尚的技术!!)。
但真正接触到"面向切面编程"的时候,发现原来就是如此啊,也没什么大不了的。只不过当时被它的名字给唬住了...
不知道各位在刚接触这些名字集群/分布式/微服务/soa
的时候,有没有被唬住了呢??
以下内容来源维基百科:
计算机集群简称集群是一种计算机系统,它通过一组松散集成的计算机软件和/或硬件连接起来高度紧密地协作完成计算工作。在某种意义上,他们可以被看作是一台计算机。集群系统中的单个计算机通常称为节点,通常通过局域网连接,但也有其它的可能连接方式。集群计算机通常用来改进单个计算机的计算速度和/或可靠性。一般情况下集群计算机比单个计算机,比如工作站或超级计算机性能价格比要高得多
集群技术特点:
在维基百科上说得也挺明白的了,我来举个例子吧。
我写了一个910便利网发布到服务器去了,现在越来越多的人访问了,访问有点慢,怎么办???很简单,(只有充钱才能变强),加配置吧(加cpu,加内存)。升级完配置之后,访问人数越来越多,于是发现又不禁用啦,在这台机器上加配置已经解决不了了,怎么办???很简单,(只有充钱才能变强),我再买一台服务器,将910便利网也发布到新买的这台服务器上去。
特点:
好处:
集群:同一个业务,部署在多个服务器上(不同的服务器运行同样的代码,干同一件事)
以下内容来源维基百科:
分布式系统是一组计算机,通过网络相互连接传递消息与通信后并协调它们的行为而形成的系统。组件之间彼此进行交互以实现一个共同的目标。
我也来举个例子来说明一下吧:
我的910便利网已经部署到两台服务器去了,但是越来越多的人去访问。现在也逐渐承受不住啦。那现在怎么办啊??那继续充钱变强??作为一个理智的我,肯定得想想是哪里有问题。现在910便利网的模块有好几个,全都丢在同一个tomcat里边。
其实有些模块的访问是很低的(比如后台管理),那我可不可以这样做:将每个模块抽取独立出来,访问量大的模块用好的服务器装着,没啥人访问的模块用差的服务器装着。这样的好处是:一、资源合理利用了(没人访问的模块用性能差的服务器,访问量大的模块单独提升性能就好了)。二、耦合度降低了:每个模块独立出来,各干各的事(专业的人做专业的事),便于扩展
特点:
好处:
分布式:一个业务分拆多个子业务,部署在不同的服务器上(不同的服务器,运行不同的代码,为了同一个目的)
集群和分布式并不冲突,可以有分布式集群
现在3y的公司规模变大了,有5个小伙子写java,4个小伙子写前端,2个小伙子做测试,1个小伙子做dba。
其实我认为分布式/微服务/soa这三个概念是差不多的,了解了其中的一个,然后将自己的理解往上面套就好了。没必要细分每个的具体概念~~(当然了,我很期待有大佬可以在评论区留言说下自己的看法哈)
参考资料:
从上面所讲的分布式概念我们已经知道,分布式简单理解就是:一个业务分拆多个子业务,部署在不同的服务器上
如果你接触过一些分布式的基础概念,那肯定会听过cap这个理论。就比如说:你学了mysql的innodb存储引擎相关知识,你肯定听过acid!
首先,我们来看一下cap分别代表的是什么意思:
下面有三个节点(它们是集群的),此时三个节点都能够相互通信:
由于我们的系统是分布式的,节点之间的通信是通过网络来进行的。只要是分布式系统,那很有可能会出现一种情况:因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。
现在出现了网络分区后,此时有一个请求过来了,想要注册一个账户。
此时我们节点一和节点三是不可通信的,这就有了抉择:
一般我们说的分布式系统,p:分区容错性(partition-tolerance)这个是必需的,这是客观存在的。
cap是无法完全兼顾的,从上面的例子也可以看出,我们可以选ap,也可以选cp。但是,要注意的是:不是说选了ap,c就完全抛弃了。不是说选了cp,a就完全抛弃了!
在cap理论中,c所表示的一致性是强一致性(每个节点的数据都是最新版本),其实一致性还有其他级别的:
可用性的值域可以定义成0到100%的连续区间。
所以,cap理论定义的其实是在容忍网络分区的条件下,“强一致性”和“极致可用性”无法同时达到。
参考资料:
扩展阅读:
相信大家读到这里,对分布式/微服务已经有一定的了解了,其实单从概念来说,是非常容易理解的。只是很可能被它的名字给唬住了。
下面我就来讲讲springcloud最基础的知识~
前面也讲了,从分布式/微服务的角度而言:就是把我们一大的项目,分解成多个小的模块。这些小的模块组合起来,完成功能。
举个可能不太恰当的例子(现实可能不会这么拆分,但意思到位就好了):
拆分出多个模块以后,就会出现各种各样的问题,而springcloud提供了一整套的解决方案!
springcloud的基础功能:
springcloud的高级功能(本文不讲):
那会出现什么问题呢??首当其冲的就是子系统之间的通讯问题。子系统与子系统之间不是在同一个环境下,那就需要远程调用。远程调用可能就会想到httpclient,webservice等等这些技术来实现。
既然是远程调用,就必须知道ip地址,我们可能有以下的场景。
http://123.123.123.123:8888/java3y/3
http://123.123.123.123:8888/java3y/3
,(同样地)b->c,c->dhttp://123.123.123.123:8888/java3y/3
,(同样地)b->c万一,我们b服务的ip地址变了,想想会出现什么问题:a服务,d服务(等等)需要手动更新b服务的地址
为了解决微服务架构中的服务实例维护问题(ip地址), 产生了大量的服务治理框架和产品。 这些框架和产品的实现都围绕着服务注册与服务发现机制来完成对微服务应用实例的自动化管理。
在springcloud中我们的服务治理框架一般使用的就是eureka。
我们的问题:
eureka是这样解决上面所说的情况的:
a、b、c、d四个服务都可以拿到eureka(服务e)那份注册清单。a、b、c、d四个服务互相调用不再通过具体的ip地址,而是通过服务名来调用!
eureka专门用于给其他服务注册的称为eureka server(服务注册中心),其余注册到eureka server的服务称为eureka client。
在eureka server一般我们会这样配置:
register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
eureka client分为服务提供者和服务消费者。
如果在网上看到springcloud的某个服务配置没有"注册"到eureka-server也不用过于惊讶(但是它是可以获取eureka服务清单的)
eureka: client: register-with-eureka: false # 当前微服务不注册到eureka中(消费端) service-url: defaultzone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
下面是eureka的治理机制:
最后,我们就有了这张图:
举个例子:
优秀博文:
通过eureka服务治理框架,我们可以通过服务名来获取具体的服务实例的位置了(ip)。一般在使用springcloud的时候不需要自己手动创建httpclient来进行远程调用。
可以使用spring封装好的resttemplate工具类,使用起来很简单:
// 传统的方式,直接显示写死ip是不好的! //private static final string rest_url_prefix = "http://localhost:8001"; // 服务实例名 private static final string rest_url_prefix = "http://microservicecloud-dept"; /** * 使用 使用resttemplate访问restful接口非常的简单粗暴无脑。 (url, requestmap, * responsebean.class)这三个参数分别代表 rest请求地址、请求参数、http响应转换被转换成的对象类型。 */ @autowired private resttemplate resttemplate; @requestmapping(value = "/consumer/dept/add") public boolean add(dept dept) { return resttemplate.postforobject(rest_url_prefix + "/dept/add", dept, boolean.class); }
为了实现服务的高可用,我们可以将服务提供者集群。比如说,现在一个秒杀系统设计出来了,准备上线了。在11月11号时为了能够支持高并发,我们开多台机器来支持并发量。
现在想要这三个秒杀系统合理摊分用户的请求(专业来说就是负载均衡),可能你会想到nginx。
其实springcloud也支持的负载均衡功能,只不过它是客户端的负载均衡,这个功能实现就是ribbon!
负载均衡又区分了两种类型:
所以,我们的图可以画成这样:
ribbon是支持负载均衡,默认的负载均衡策略是轮询,我们也是可以根据自己实际的需求自定义负载均衡策略的。
@configuration public class myselfrule { @bean public irule myrule() { //return new randomrule();// ribbon默认是轮询,我自定义为随机 //return new roundrobinrule();// ribbon默认是轮询,我自定义为随机 return new randomrule_zy();// 我自定义为每台机器5次 } }
实现起来也很简单:继承abstractloadbalancerrule类,重写public server choose(iloadbalancer lb, object key)
即可。
springcloud 在cap理论是选择了ap的,在ribbon中还可以配置重试机制的(有兴趣的同学可以去搜搜)~
举个例子:
优秀博文:
到目前为止,我们的服务看起来好像挺好的了:能够根据服务名来远程调用其他的服务,可以实现客户端的负载均衡。
但是,如果我们在调用多个远程服务时,某个服务出现延迟,会怎么样??
在高并发的情况下,由于单个服务的延迟,可能导致所有的请求都处于延迟状态,甚至在几秒钟就使服务处于负载饱和的状态,资源耗尽,直到不可用,最终导致这个分布式系统都不可用,这就是“雪崩”。
针对上述问题, spring cloud hystrix实现了断路器、线程隔离等一系列服务保护功能。
hystrix提供几个熔断关键参数:滑动窗口大小(20)、 熔断器开关间隔(5s)、错误率(50%)
hystrix还有请求合并、请求缓存这样强大的功能,在此我就不具体说明了,有兴趣的同学可继续深入学习~
hystrix仪表盘:它主要用来实时监控hystrix的各项指标信息。通过hystrix dashboard反馈的实时信息,可以帮助我们快速发现系统中存在的问题,从而及时地采取应对措施。
启动时的页面:
监控单服务的页面:
我们现在的服务是这样的:
除了可以开启单个实例的监控页面之外,还有一个监控端点 /turbine.stream
是对集群使用的。 从端点的命名中,可以引入turbine, 通过它来汇集监控信息,并将聚合后的信息提供给 hystrixdashboard 来集中展示和监控。
举个例子:
参考资料:
上面已经介绍了ribbon和hystrix了,可以发现的是:他俩作为基础工具类框架广泛地应用在各个微服务的实现中。我们会发现对这两个框架的使用几乎是同时出现的。
为了简化我们的开发,spring cloud feign出现了!它基于 netflix feign 实现,整合了 spring cloud ribbon 与 spring cloud hystrix, 除了整合这两者的强大功能之外,它还提
供了声明式的服务调用(不再通过resttemplate)。
feign是一种声明式、模板化的http客户端。在spring cloud中使用feign, 我们可以做到使用http请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个http请求。
下面就简单看看feign是怎么优雅地实现远程调用的:
服务绑定:
// value --->指定调用哪个服务 // fallbackfactory--->熔断器的降级提示 @feignclient(value = "microservicecloud-dept", fallbackfactory = deptclientservicefallbackfactory.class) public interface deptclientservice { // 采用feign我们可以使用springmvc的注解来对服务进行绑定! @requestmapping(value = "/dept/get/{id}", method = requestmethod.get) public dept get(@pathvariable("id") long id); @requestmapping(value = "/dept/list", method = requestmethod.get) public list<dept> list(); @requestmapping(value = "/dept/add", method = requestmethod.post) public boolean add(dept dept); }
feign中使用熔断器:
/** * feign中使用断路器 * 这里主要是处理异常出错的情况(降级/熔断时服务不可用,fallback就会找到这里来) */ @component // 不要忘记添加,不要忘记添加 public class deptclientservicefallbackfactory implements fallbackfactory<deptclientservice> { @override public deptclientservice create(throwable throwable) { return new deptclientservice() { @override public dept get(long id) { return new dept().setdeptno(id).setdname("该id:" + id + "没有没有对应的信息,consumer客户端提供的降级信息,此刻服务provider已经关闭") .setdb_source("no this database in mysql"); } @override public list<dept> list() { return null; } @override public boolean add(dept dept) { return false; } }; } }
调用:
基于上面的学习,我们现在的架构很可能会设计成这样:
这样的架构会有两个比较麻烦的问题:
还是画个图来理解一下吧:
每个服务都有自己的ip地址,nginx想要正确请求转发到服务上,就必须维护着每个服务实例的地址!
http://123.123.123.123 http://123.123.123.124 http://123.123.123.125 http://123.123.123.126 http://123.123.123.127
购物车和订单模块都需要用户登录了才可以正常访问,基于现在的架构,只能在购物车和订单模块都编写校验逻辑,这无疑是冗余的代码。
为了解决上面这些常见的架构问题,api网关的概念应运而生。在springcloud中了提供了基于netfl ix zuul实现的api网关组件spring cloud zuul。
spring cloud zuul是这样解决上述两个问题的:
zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服务调用的客户端负载均衡功能。也就是说:zuul也是支持hystrix和ribbon。
关于zuul还有很多知识点(由于篇幅问题,这里我就不细说了):
zuul支持ribbon和hystrix,也能够实现客户端的负载均衡。我们的feign不也是实现客户端的负载均衡和hystrix的吗?既然zuul已经能够实现了,那我们的feign还有必要吗?
或者可以这样理解:
有了zuul,还需要nginx吗?他俩可以一起使用吗?
参考资料:
随着业务的扩展,我们的服务会越来越多,越来越多。每个服务都有自己的配置文件。
既然是配置文件,给我们配置的东西,那难免会有些改动的。
比如我们的demo中,每个服务都写上相同的配置文件。万一我们有一天,配置文件中的密码需要更换了,那就得三个都要重新更改。
在分布式系统中,某一个基础服务信息变更,都很可能会引起一系列的更新和重启
spring cloud config项目是一个解决分布式系统的配置管理方案。它包含了client和server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。
springcloud config其他的知识:
使用springcloud config可能的疑问:application.yml和 bootstrap.yml区别
本文主要写了springcloud的基础知识,希望大家看完能有所帮助~
springcloud的资料也很多,我整理一些我认为比较好,想要深入的同学不妨看看下边的资源~~~
springcloud系列文章参考资料:
参考书籍:
springcloud github demo(看完文章的同学可以自己练手玩玩,写好了readme了):
如果想看更多的原创技术文章,欢迎大家关注我的微信公众号:java3y。java技术群讨论:742919422。公众号还有海量的视频资源哦,关注即可免费领取。
可能感兴趣的链接:
如对本文有疑问, 点击进行留言回复!!
springmvc之ResponseBody响应json数据遇到的错误及解决
uni-app 后台升级 静默升级 uniapp 后台更新 静默更新 在线升级
SpringBoot多Module启动报错Could not transfer metadata
Hibernate项目报错:Cannot call sendError() after the response has been committed
网友评论