微服务综合部署——SpringBoot项目制作Docker镜像注册在Zookeeper并利用K8S技术部署

Source

一、如果你要从头开发一个基于微服务架构的项目,项目中的服务最终要部署在k8s管理的镜像环境中,你认为应如何创建项目?与本次实验过程相比,哪些改进可以让开发更加合理高效?

第一步:搭建项目并制作合适的jar包

        我们可以仿照实验二中的框架来搭建我们的服务,首先搭建一个Springboot的项目,随后在这个项目中定义我们的接口规范,并且要按照预期的要求编写业务逻辑代码,实现具体的功能;其次我们可以利用Zookeeper和Dubbo来管理我们的服务,将我们的服务注册到Zookeeper服务中心,利用Dubbo的客户端界面来进行管理;最终我们要选择比较稳定的JDK版本和其他类似的配置环境,确保我们的服务可以稳定的运行,确认后利用IDEA集成的功能将整个项目打包成jar包。在这里可以直接在Windows环境下实现,打开CMD输入java -jar +XXX.jar检测是否可以正常运行,若正常运行,Dubbo是否可以检测到。其次要注意,由于要部署到K8S中,我们的Zookeeper的IP地址要将默认的127.0.0.1更改为服务器的IP,否则服务器上部署的Dubbo将无法识别到容器内的服务程序。

第二步:将jar包制作成Docker镜像并进行部署

        我们可以直接通过Dockerfile来将上一步生成的jar包制作成dubbo镜像,随后生成docker镜像后,我们可以使用docker images指令来查看本地是否生成了镜像;如果需要部署到非本地的服务器集群,那么我们需要将镜像打好标签,push到dockerhub上。随后,我们编写yaml文件来对服务进行部署。这里要注意,yaml文件和dockerfile文件中一定要加入java -jar启动指令,同时要规定好服务暴露的端口以及主机的端口,并且要保持统一。做好这些后,我们将这个deployment apply,利用K8S的指令kubectl get pods -a或kubectl describe pods来详细查看镜像的运行状态。有的时候,即便是status显示running,依然可能发生一些问题。我们一定要进入到容器内部,利用describe来检查容器内镜像是否正常启动并持续提供服务。如果这一切处理得当,我们可以在zookeeper下的bin文件夹下,打开zkCli.sh并运行./zkCli.sh。通过输入ls /dubbo/加服务的UserManager/providers即可查看到提供服务的容器的IP,这些IP是相互独立的,与我们在kubectl describe pods看到的是一致的。此时,我们调用消费者,就可以享用来自不同容器的提供者所提供的的服务了;另外,我们可以直接利用K8S技术直接对容器进行管理。

改进:

  1. 明确服务的具体需求,减少对java源代码的修改。由于部署的过程是十分繁琐的,如果一开始的源代码就存在问题,那么会一遍又一遍的进行部署,会浪费大量的开发时间。因此尽量保证一遍过。
  2. IDEA中有类似的插件,可以直接将本地与提供docker服务环境的服务器相连,我们可以利用这个插件直接将项目以镜像的形式部署到远端,而避免了中间复杂而繁琐且易出错的部署过程。这将大大节约我们的开发时间。

二、如何将实验中多个provider服务实例的负载均衡策略改为轮询调用?即在多次反复调用时轮流调用各个服务实例

        Dubbo官方提供了轮询负载均衡算法,通常是通过“轮询+权重”来进行实现。

 

算法步骤

1、计算服务提供者的总权重。

2、每个服务提供者除了始终不变的固定权重以外,需要记录服务提供者当前权重。

3、每次请求,更新所有的服务提供者的当前权重,当前权重 = 当前权重 + 固定权重。

4、从上一步得到的结果中,选择一个当前权重最大的服务提供者用于处理请求,如果存在多个,那么就看遍历的过程中先遇到哪个服务提供者就是哪一个。并且,选中的权重最大的服务提供者更新其当前权重 = 当前权重 - 总权重。

 

举一个例子

三个服务提供者的固定权重分别是10,20,50,假设其当前权重依次为0,0,0

1、计算总权重 10+20+50 = 80

2、当请求来了,更新三个服务提供者的当前权重,当前权重 = 当前权重+固定权重,依次结果为10,20,50.

3、从中选择最大的一个,也就是第三个用于处理请求,同时将其当前权重更新:当前权重 = 50 - 80 = -30

4、所以此时三个服务提供者的当前权重依次为10,20,-30

5、当第二个请求来的时候,更新三个服务提供者的当前权重,当前权重 = 当前权重+固定权重,依次结果为20,40,20

6、从重选择最大的一个,也就是第二个用于处理请求,同时将其当前权重更新:当前权重 = 40 - 80 = -40.

7、依次类推。

如果我们想使用均衡的轮询策略,那么我们只需要将上述的权重设置为均等的正值即可。具体的算法实现如下所示:

先来看一下WeightedRoundRobin对象,一个服务提供者节点,对应一个WeightedRoundRobin对象,该对象记录了服务提供者的权重,当前的权重以及上一次权重更新的时间。

 RoundRobinLoadBalance中的doSelect方法如下:

 

 

        methodWeightMap 方法权重Map用于保存服务方法提供者集群的WeightedRoundRobin对象集合。一个服务方法提供者对应一个WeightedRoundRobin。key是服务提供者的身份信息,value是WeightedRoundRobin对象。

        遍历所有的服务提供者,

        根据它的身份信息以及服务提供者的权重。

        基于身份信息得到WeightedRoundRobin对象,如果没有的话new一个。

        若缓存中的WeightedRoundRobin的权重 不等于刚得到的weight,那么就更新WeightedRoundRobin中的weight。

        WeightedRoundRobin中的当前权重自增,当前权重 = 当前权重 + 固定权重,并设置更新的时间。记录当前权重最大的节点的信息,并统计总权重。

        若服务提供者列表和map的大小不一致,说明有服务提供者挂了,那么根据WeightedRoundRobin中的lastUpdate字段,判断是否存活,移除挂了的服务提供者。对找到的最大的当前权重的服务提供者进行更新当前权重,它的当前权重 = 当前权重 - 总权重。返回最大的服务提供者节点。

        此外,还有一种利用计数器取模的方法:

        如果所有服务器的该接口方法的权重一样,则直接内部的序列计数器( sequences ) +1 然后对服务器的数量进行取模来决定调用哪个服务器上的服务;如果服务器的该接口方法的权重不一样(就是说存在预热中的服务器),则找到其中最大的权重,然后将内部的权重计数器( weightSequences ) +1 并对该最大权重数取模,然后再找出权重比该取模后的值大服务器列表,最后通过内部的序列计数器( sequences ) +1 然后对服务器列表数量进行取模来决定调用哪个服务器上的服务。

 三、实验过程中遇到的主要问题与解决办法。

        1.jar包无法找到主清单而无法运行

                加入插件

                 启动类不要放在test下,尽量放在源代码目录下

        2.打包编码报错

         3.jar在本地和服务器可以运行,但是在容器里无法持续运行

                这个问题实际上就是自己的业务逻辑的问题,一旦发现了,不要去思考到底是为什么, 

        首先尝试加入一些死循环是否可以保持进程的活性。

         4.Dubbo注册中心找不到容器中的服务

             一般出现这个问题,主要由以下几个原因:

        1.容器假running,实际上根本就没有注册上,他一直就没有成为provider。这种情况通常是由于jar包本身无法正常运行所导致,应当立即检查源代码,或者拉取的镜像是不是你的最新版本。

        2.容器跑起来了,但是就是找不找。存在两种可能,第一种,你忘记了将zookeeper注册中心的IP更改为服务器IP;第二种,业务逻辑有问题。程序在注册成功后立即终止,running的进程时间99.99%都是在启动,注册上的一瞬间就崩溃了,dubbo甚至来不 及产生任何反应。这时就应该检查业务逻辑了。

        5.如何让消费者显示调用的服务者的IP 

        我们可以在provider中添加功能,迫使其自动输出所在容器的IP。我们只需利用java自带的网络工具包即可,具体实现逻辑和代码如下图所示: