这周浅显的学习了springcloud.简单聊一下微服务.所谓的微服务远远没有我想想的那么高端难以理解,简单说,就是多个服务分布在不同的服务器上,由这些服务互相配合完成某一项任务.那服务和服务之间调用的方式就得用Restful,通俗讲就是服务和服务之间通过访问对方的controller而完成服务调用.这样的局面就是在同一个生态系统中的某个服务,它既可能是服务端(提供数据)也可能是客户端(发出请求).
接下来我们假设当前有订单服务(A),购物车服务(B),这两个服务都可以互相调用,订单可以读取购物车的商品形成订单,当下单完成,订单要负责调用购物车服务清除已经购买的商品.现在有个问题出现了,设A服务的服务地址为 http://39.105.59.232:8443 设B的服务地址为http://39.105.59.233:8344 此时我们要调用API是不是需要把IP和端口硬编码到代码中,好一点的方式放到配置文件.可是这不太好,既然我们是微服务,我们是分布式,那必须得高大上.
高大上的第一步引入Eureka(注册微服务)
这个Eureka是个什么玩意呢?我们从他的功能来说,他可以注册服务和发现服务.什么意思.咱还要通俗讲,首先我们创建一个Eureka服务,咱们这里不关注具体的配置,只BB过程.过程唠明白了,配置这东西咱还不会百度吗?好了现在这个Eureka服务创建好了,他会不断地发现服务,比如咱们的AB两个服务,他发现了这两个服务,会把他们给保存起来.此时的保存其实没有那么想想中牛逼,咱们就当他只保存了服务的IP和端口.回过头说,Eureka要想发现服务,还需要服务本身做好配置.我们在A服务中配置,将A服务本身注册到Eureka中,在这个注册过程中,A服务会给自己起个牛逼的名字.例如:AAA 到此时,其实已经介绍完毕了,A服务给自己起了个牛逼的名字AAA并且告诉了Eureka,Eureka也保存了这个牛逼的名字,Eureka当然不会只记住这个名字,人家还会记住这个名字背后代表的IP.但是你需要注意了,如果你要在B服务中使用AAA这个名字去访问A服务器,那么你的B服务器必须也要配置eureka服务,此时的配置可以不将自身注册到Eureka只是用来发现服务.简单说,就是B服务也要用Eureka,但是他不用把自身注册到Eureka,只需要从Eureka服务中发现服务就可以了.Eureka本身是支持集群的,也就是说,我们可以开两个Eureka服务,将A服务注册到E1中,同时也可以注册到E2中,这时,就算其中一个Eureka崩掉了,AAA这个牛逼的名字也可以正常使用.同时Eureka也可以把自身注册到另外一台Eureka中.除此之外Eureka还有一个功能就是将服务长时间不发送心跳的服务踢掉.注册到Eureka中的服务需要在一定时间告诉Eureka一次,我还活着.当一个服务长时间不联系Eureka,那么Eureka将判定该服务挂掉了.这一项强大的功能在某个服务只有一台时不会有好处体现,毕竟如果订单服务只有A这一台服务器,如果A挂掉了,就算你不把A踢掉了,用户也还是访问不了.
高大上的第二步引入Ribbon(负载均衡)
上边咱们说到,如果只有一个A订单服务,那么A挂掉了,咋办,用户下不了单?这不妥,这样的东西怎么能出现在一个健康的生态环境呢?所以Ribbon来了.他可以做负载均衡,好了,很直白了.玩过nginx的都知道,A1,A2两个服务器,A1挂掉了,nginx引导我们访问A2.Ribbon大概做的也是这个事情.但是首先说一点,Ribbon本身是依赖Eureka的,那么怎么配置Ribbon呢?其实不用,当我们把A服务注册到Ribbon时,B服务通过A服务那个牛逼的AAA服务名调用时,Ribbon已经在工作了.这里我们需要唠一唠Ribbon实现负载均衡的步骤.当客户端去通过AAA这个别名去访问A服务器时,Ribbon会先从先找到Eureka集群,通过他自己的牛逼算法,算出来,当前哪一个Eureka状态较好,然后他就从这个Eureka中拿到被注册的所有别名和他们的实际IP.如果AAA这个别名对应的只有A1这一个订单服务器,那没得选,就用它,如果AAA这个别名对应了两个订单服务A1,A2那么Ribbon将使用他牛逼的算法,找出状态较好的订单服务,然后访问它.(PS对算法不了解的我,只能说牛逼的算法..所以大家还是要多看书)
高大上的第三步引入Feign(另一种访问方式)
服务之间的调用就真的只能 httpClient.post(url,responseObj)这种方式吗?No!身为一个高端的程序员,我们习惯抽象,习惯用注解,我们要直接,我们要面向接口.所以Feign就诞生了.我们将创建一个接口,在这个接口上放上注解,放上需要访问的地址.别的我们什么都不做,然后客户端调用这个接口,由这个接口自己搞什么httpClient.(PS对于某些程序员来说,这个操作好像显示的很浮夸,通过httpClient访问不好吗?何况springboot已经帮我们最大限度的封装了.何必在创建一个莫须有的接口呢?嗯,实际上我就是这类人,不过,人家有这个东西,并且能在springcloud生态系统中混得风生水起,那自然有他们自己的高明之处可以不用,但是不要diss)
高大上的第四步引入是Hystrix(熔断器)
这个熔断器讲真的,我觉得他的名字比负载均衡还牛逼.熔断,熔断,莫名我就想起一个大炉子,武侠小说中打造兵器的那种大熔炉.书归正题,其实没有那么神奇啦.我们思考一个场景,如果A订单服务调用B购物车服务,这是购物车服务他内部计算错误了,比如1/0了.呵呵呵.(PS在模拟抛出异常时,我百分比用这个,不用抛出,也不用捕获.写的还少.)那这事B购物车服务将不能正常返回数据给A购物车.那怎么办,A服务也要跟着抛异常.这不好,非常不好,不能因为你自己搞事情,就要整个生态系统一块搞事情啊.那么我们能怎么解决呢?两种方式,第一:在A调用处做判断,如果拿到的数据结构不是合格的,客户端做容错处理.第二:在B服务提供处做处理,如果发现自己抛异常了,提前捕获掉,不要连累调用者,并且返回一个友好的格式通知调用者,我内部搞事情了,你自己保重.这是两种常规做法,那略微高级点,我们可以做AOP操作,用过springboot的同志们都知道,在springboot中有一个注解可以在Controller节点处理异常,这其实也是AOP操作.好了.说了这么多我们还是看看Hystrix是怎么玩的.对于客户端容错和服务端容错Hystrix都可以支持.对于服务端来说,只用把在controller方法处使用注解,给注解一个值这个值可以是某个类的某个方法,如果是本类方法,则可以直接写方法名.这样在异常出现是,Hystrix就执行这一个备用方法,返回客户端信息.至于客户端,Feign是支持Hystrix的,之前我们说过,当我们使用Feign声明服务时,可以只写一个接口,那在Feign上使用熔断器只需要写一个服务的实现类就可以了,服务的哪个方法异常,Hystrix就调用实现类的哪个方法.这也许也是Feign立足之地的一方面吧.
高大上的第五步引入Zuul(路由网关)
还记得我们最开始给A服务配置的AAA这个牛逼的别名不?我们虽然给A服务配置了别名,但是这个别名也只能是在生态系统内部使用,在具体点,只有发现Eureka服务才可以使用这个别名对A服务进行访问.如果你从浏览器访问http://AAA/product/get ... 那肯定是访问不到的.但是引入了Zuul网关就不同了,首先我们做一个Zuul网关,并将这个网关注册到Eureka服务,自然而然Zuul就可以在Eureka中找到存储别名的名单.这是我们访问Zuul服务+别名也是可以访问A服务的.例如的Zuul服务地址是http://39.105.59.252:8442/zuul那么我们就能用http://39.105.59.252:8442/zuul/AAA/product/get来请求订单服务了.除此之外,我们还可以对AAA这个服务在进行一个别名映射.我们配置一个指定的路径例如/product/**然后在配置一个具体的Eureka映射路径AAA这样我们访问http://39.105.59.252:8442/zuul/product的所有请求都会被转发到AAA这个微服务中.除此之外,对于每一个Zuul我们还能增加一层过滤器,通过继承Zuul的抽象类,然后覆盖其中的方法,就可以对该网关进行过滤.
关于springcloud生态系统还有很多有用的中间件可以使用,每个中间件的自己的功能特性,我们在项目中,可以根据实际情况选择适用当前项目的中间件.
最后附录上关于以上内容的git地址 https://gitee.com/zumengjie/springcloud