摘要
今天终于有了点空闲时间,所以更新了一下代码生成器,修复了用户反馈的bug,本次更新主要增加了dubbo和springcloud脚手架的下载功能,架子是本人亲自搭建,方便自由扩展或者小白学习使用,你也许会问为什么它们不能像springboot一样通过配置的方式生成项目,我只能回答:理论上是可以的,以后有时间会摸索可行之道,但是我觉得微服务框架业务复杂多变,不如直接使用脚手架自由扩展来的方便,所以目前是采用了这样一种方式。本篇博客将具体介绍脚手架每个部分的功能模块,然后畅想一下微服务技术的未来发展趋势。
dubbo脚手架
dubbo曾经风靡一时,但是逐步被springcloud所替代,所以dubbo的脚手架我做的相对简易,以表缅怀之情。
代码生成器获取dubbo脚手架十分简单,如下:
鼠标点击一下便可自动下载到桌面
然后看一下脚手架的目录结构:
项目导入到了idea当中,因为dubbo需要zookeeper依赖,所以需要配置zookeeper注册中心,相信你可以自己解决~
具体的细节请参考我的dubbo+zookeeper对比springCloud及分布式项目搭建详解此篇博客,这个脚手架就是博客当中讲述的demo项目。
springcloud脚手架
把springcloud脚手架下载下来导入到idea当中,项目结构如下:
下面对每个module的配置进行简要讲解,其他的细节读者可以使用生成器下载自行查看。
cloud_eureka模块
此模块主要负责服务发现注册,相当于dubbo的zookeeper,只不过springCloud使用eureka来进行服务的发现和注册,相信大家都知道CAP原则,即一致性,可用性和分区容错性,只要是分布式项目,一般都具备分区容错性(简单理解,一个节点挂了,其他节点可以正常提供服务)。
zookeeper本身就不是为高可用设计的,节点之间的数据会保持高度的同步,并且一旦发生网络隔离,zookeeper内部会进行master选举,这个选举流程是十分缓慢的,长达30到120秒,对于一个要不断向外界提供服务的系统来说,这将是非常致命的!所以dubbo+zookeeper总体来说符合CP原则。
springcloud的服务注册中心eureka则不同,eureka每个节点都是平等的,不会有选举master节点这一说法,并且本身具有自我保护机制,具备服务的高度可用性,相对的,它无法做到数据的强一致,也就是无法保证在每个节点上始终获取的都是最新的数据,但我们可以在程序设计的时候保证结果的最终一致性。所以springcloud总体来说符合AP原则。
让我们看一下cloud_eureka的yml配置,如下:通过加载不同的yml,就可以分别启动server1和server2构成eureka集群,负责服务发现和注册的职责。
application.yml:
#注册中心应用名称
spring:
application:
name: eureka-server
#使用的配置文件名 `java -jar -Dspring.profiles.active=serverX demo.jar`启动serverX配置
profiles:
active: server1
application-server1.yml:
#注册中心运行的端口号
server:
port: 8001
#注册中心应用名称
#spring:
# application:
# name: eureka-server
#eureka.server.enableSelfPreservation:是否向注册中心注册自己
#通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server.
eureka:
# 自我保护机制
#server:
#enableSelfPreservation: false #关闭eureka的自我保护 小规模项目关闭比较好
#eviction-interval-timer-in-ms: 5000 #清理间隔时间,单位为毫秒(默认值60 * 1000)
#use-read-only-response-cache: false
instance:
hostname: server1
prefer-ip-address: false
# ip-address: 172.193.225.185
# instance-id: ${spring.cloud.client.ipAddress}:${server.port}
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://server2:8002/eureka/
application-server2.yml:
#注册中心运行的端口号
server:
port: 8002
#注册中心应用名称
#spring:
# application:
# name: eureka-server
#eureka.server.enableSelfPreservation:是否向注册中心注册自己
#通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server.
eureka:
# 自我保护机制
#server:
#enableSelfPreservation: false #关闭eureka的自我保护 小规模项目关闭比较好
#eviction-interval-timer-in-ms: 3000 #清理间隔时间,单位为毫秒(默认值60 * 1000)
#use-read-only-response-cache: false
instance:
hostname: server2
prefer-ip-address: false
# ip-address: 172.193.225.185
# instance-id: ${spring.cloud.client.ipAddress}:${server.port}
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://server1:8001/eureka/
cloud_zuul模块
此模块是所有微服务的网关,这里采用的是zuul,现在spring官方逐渐放弃了zuul,而是采用了自己的gateway网关组件,因为zuul是io阻塞的,但在配置上可以类比zuul的配置。网关组件也是需要在eureka中进行注册的。
application.yml如下,ribbon和hystrix的超时时间需要特别配置一下,不然项目启动之后第一次通过网关访问服务大概率会报hystrix timeout的超时错误(默认超时时间很短),ribbon负责负载均衡,hystrix负责服务熔断,所以配置上服务熔断的时间应该大于负载均衡的总时间,否则会一直有warn提示。
服务熔断的超时时间计算公式如下:
(1+MaxAutoRetries + MaxAutoRetriesNextServer)* ReadTimeout,加1是因为首次访问不计入重试次数,MaxAutoRetries为同一台实例最大调用次数,默认为1,MaxAutoRetriesNextServer为切换其他实例的最大次数,默认为1,所以熔断器的超时时间要大于重试时间,不然重试就失去了意义,这里通过计算超时时间为20000,所以hystrix的超时时间设置为比20000稍大的30000即可。
#网关
spring:
application:
name: cloud-zuul
eureka:
client:
service-url:
defaultZone: http://server1:8001/eureka/,http://server2:8002/eureka/
instance:
prefer-ip-address: true
server:
port: 7001
zuul:
routes:
cloud-service1: #测试service1
path: /service1/**
serviceId: cloud-service1
cloud-service2: #测试service1
path: /service2/**
serviceId: cloud-service2
host:
connect-timeout-millis: 15000
socket-timeout-millis: 10000
ribbon:
ConnectTimeout: 5000
ReadTimeout: 5000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
cloud_config模块
此模块为springcloud配置中心,可以从远程git仓库拉取配置文件,同时它不需要注册到eureka当中。
application.yml内容如下:rabbitmq的配置主要是实现服务总线的作用。用户向config发送http://localhost:6001/actuator/bus-refresh请求,config会向rabbitmq发送配置更新的消息,同时配置了服务总线的微服务模块会监听到此消息,就会重新从远程拉取配置文件并重新加载,这样我们在远程仓库修改了配置文件无需重启项目就可以实现配置的更新。
server:
port: 6001
spring:
application:
name: cloud-config
cloud:
config:
server:
git:
uri: https://gitee.com/zrxjava/cloudModel_config.git
rabbitmq:
host: 127.0.0.1
username: guest
password: guest
#刷新总线的接口
management:
endpoints:
jmx:
exposure:
exclude: bus-refresh
cloud_service1模块
service1为其中的一个微服务模块,同时它也注册在eureka当中,在service1中,使用了feign来调用service2,我们知道,微服务可以通过ribbon和feign来调用,不同的是,ribbon采用restTemplate的方式调用,feign则是把ribbon封装了一层,采用接口形式调用,并且默认支持负载均衡,同时也可选择开启服务熔断,feign客户端代码如下:
package consumer.client;
import consumer.client.impl.Service2ClientImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
@Component //仅仅是为了屏蔽idea误显示的注入错误
@FeignClient(value = "cloud-service2", fallback = Service2ClientImpl.class)
public interface Service2Client {
@GetMapping("/test/do")
public String test();
}
package consumer.client.impl;
import consumer.client.Service2Client;
import org.springframework.stereotype.Component;
//熔断器执行的方法
@Component
public class Service2ClientImpl implements Service2Client {
@Override
public String test() {
return "触发熔断器!";
}
}
service1的配置文件通过config配置中心管理,启动service1的时候会加载bootstrap.yml的配置,从gitee上拉取配置文件,config配置中心主要是为了方便运维,配置上springcloud-bus后可以无需重启项目加载配置文件,实乃运维之福音。bootstrap.yml内容如下:
spring:
cloud:
config:
name: service1 #对应git服务器上的name #name-profile
profile: dev #对应git服务器上的profile
label: master #提交到master分支
uri: http://localhost:6001 #config的访问地址
gitee仓库service-dev.yml内容如下:
#注册中心应用名称
spring:
application:
name: cloud-service1
rabbitmq:
host: 127.0.0.1
username: guest
password: guest
eureka:
client:
service-url:
defaultZone: http://server1:8001/eureka/,http://server2:8002/eureka/
instance:
prefer-ip-address: true
server:
port: 9001
feign:
hystrix:
enabled: true
ribbon:
ConnectTimeout: 5000
ReadTimeout: 5000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
cloud_service2模块
service2跟service1同样注册进了eureka中,同时也仅仅是注册进入了eureka中供service1调用,在真实的项目当中,每个微服务是可以互相调用的,只是我在这里偷了个懒,没有配置feign的客户端调用,也就是说,它同样可以调用service1。
application.yml如下:
#注册中心应用名称
spring:
application:
name: cloud-service2
eureka:
client:
service-url:
defaultZone: http://server1:8001/eureka/,http://server2:8002/eureka/
instance:
prefer-ip-address: true
server:
port: 9002
测试
首先我们启动两个eureka构成集群,分别使用server1和server2文件启动项目(先启动的一方由于另一方没有启动会报错,当另一方启动完毕报错即会自动消失,因为两方是互相注册形成高可用集群),启动完毕后,访问http://localhost:8001/即可看到server1界面,如下:
同样,server2也是可以正常访问的,这里就不再截图了。
接下里启动配置中心,注意service1的yml需要通过config远程获取,所以需要先启动配置中心config,然后再分别启动service1和service2,最后启动zuul。如果没有配置rabbitmq,启动config和service1会报错失败(提示无法连接rabbitmq服务),不启动它们也可。全部启动完毕后,发现eureka注册中心已经有了各自的服务注册信息,如图:
如上图,我们成功启动了两个service微服务和zuul服务网关。在这里eureka界面的红色字体是由于eureka的自我保护机制触发的,并不是报错,当eureka收到的最后一分钟服务实例续约的总数/每分钟期望收到的续约数<85%的时候,便会触发自我保护机制告诉你可能有节点出了问题(比如网络延迟,并不是服务真的挂了),但是它不会去剔除它,会保留其注册信息,等到节点恢复正常仍然可以继续工作,这样使得整个服务更加高可用。
最后我们通过zuul网关地址访问service1的服务(service1会通过feign调用service2),访问http://localhost:7001/service1/test/do(如果没有启动config和service1,访问http://localhost:7001/service2/test/do),如下:
这样一来,整个流程就结束了,springcloud的核心组件也就讲解完毕。
微服务遐想
值得一提的是,在真实的微服务项目中,服务的数量远远不止这些,代码生成器也只是帮助你搭建了基本的架构模型。所以如果通过人工部署管理的方式会变得异常困难,但后来随着容器化技术的发展,通过dockerfile把每个服务生成镜像在dokcer容器中运行渐渐成为一种主流的部署运维方式,并且通过maven插件可以一键构建dockerfile文件生成镜像并上传至docker私服,通过docker私服便可以上传我们自己的微服务镜像文件并十分方便的运行它们,通过jenkins持续集成,同时也让微服务发布更加的容易,最后为了统一管理docker容器,k8s超过docker swarm成为容器化应用的新一代宠儿,时代在飞速发展,技术的迭代也同样迅速!
前阵子跟朋友微信闲聊,了解到Service Mesh技术正在悄然兴起,大厂已经有了落地应用,简单理解,service mesh把每个微服务做了进一步的解耦,把通信相关的操作(负载均衡,断路器等)抽离了出来,为每个微服务生成了一个代理服务,而微服务做到了真正只需要关心业务。以前的微服务是单轮车,现在变成了双轮车,所以有人也称它为“边车模式“,把每个代理服务用线连接起来,组成了一个错综复杂的网格,service mesh也就由此而来,后来演化出了集中式的控制面板来管理一个个的网格,代表作品:Istio。
但同样的,由于service mesh接管了网络流量,系统的稳定性就会依赖于service mesh,同时相比之前,额外引入的大量service mesh实例对运维和管理来说也是一个巨大的挑战。
service mesh是未来微服务的发展趋势,历史也总是惊人的相似,最开始的时候,人们为了解决端到端的通信问题,tcp协议横空出世,多机通信从此变得简单可靠。如今我们来到了微服务时代,为了屏蔽分布式系统的通信复杂性,service mesh应运而生。这让我们回归业务,聚焦真正的价值!
代码生成器:点击下载