随着微服务的流行,基于spring生态的spring cloud越来越被关注, Spring Cloud专注于提供良好的开箱即用的典型用例和可扩展性覆盖机制。
Spring cloud包含两个基本模块,spring cloud context和spring cloud commons。
spring cloud context即spring cloud应用上下文,包含引导上下文(加载bootstrap配置)、配置加密、配置刷新范围(RefreshScope)、控制端点(/env/reset,/refresh,/restart等)功能。
Spring cloud commons 提供服务注册发现,负载均衡,断路器等模式的一个共用抽象层,为具体现实提供统一抽象。
实际应用过程中,我们使用了
Consul 是使用Go开发的开源的服务发现和健康检查的工具.Consul还提供K/V存储.可以很方便的使用Consul集群搭建高可用的微服务基础架构. Consul还包含web ui 可以查看各服务状态.
线上consul地址: http://consul.nidianwo.com/
如下截图:
因为consul内置了服务注册发现功能,所以spring cloud consul代码实现上比较简单,使用ConsulServiceRegistry进行服务注册操作,使用ConsulDiscoveryClient获取所有服务或者up的服务,内部则是使用ConsulClient请求consul集群进行操作。
在使用过程中出现了一个问题,一个服务多个机器注册的时候有时注册的服务会被覆盖,查看源码发现,应用servlet容器初始化事件被监听之后会进行consul应用的自动注册,注册使用ConsulAutoRegistration对象,内部会有一个NewService对象,通过NewService对象的id判断服务的唯一性,而NewService的id默认是ApplicationContext对象的id,也就是应用名称加port加profile,所以同一个应用这个id是相同的所以注册会被覆盖。 找到问题了那么就来解决问题吧,增加配置bean通过覆盖ConsulAutoRegistration对象,设置id为ApplicationContext的id加机器的ip保证应用多台机器的id唯一。
@Bean @Primary public ConsulAutoRegistration consulRegistration(ConsulDiscoveryProperties properties, ApplicationContext applicationContext, ServletContext servletContext, HeartbeatProperties heartbeatProperties) { properties.setInstanceId(applicationContext.getId()+"-"+ DigestUtils.sha1Hex(properties.getIpAddress())); return ConsulAutoRegistration.registration(properties, applicationContext, servletContext, heartbeatProperties); }
ConsulDiscoveryProperties这个类是consul服务发现的配置类,其中一些配置需要注意,比如preferIpAddress默认为false,即默认是通过host来进行服务的地址进行注册和检查,但是当环境不支持host访问时就会出问题,比如我们线上的阿里云环境,也有机器不支持host访问,所以需要配置preferIpAddress=true。
Spring Cloud Config提供服务端和客户端在分布式系统中扩展配置。支持不同环境的配置(开发、测试、生产)。使用Git做默认配置后端,可支持配置环境打版本标签。
spring cloud config架构图如下:
Spring cloud config包含4部分
# git config spring.cloud.config.server.git.uri=http://60.191.68.43:19090/config/{application}-config.git spring.cloud.config.server.git.username=xxx spring.cloud.config.server.git.password=xxx spring.cloud.config.server.git.force-pull=true (这个配置可以在配置仓库冲突的时候强制拉取,而不会取不到最新的配置) 通过如上配置git仓库地址
Config server以rest方式提供配置资源,如下所示:
我们的config client使用consul服务发现来找到config server来拉取配置,默认ConsulDiscoveryClient获取服务的时候是根据服务id获取所有服务节点,即获取了down的服务,造成拉取配置失败,查看consul服务发现的配置类ConsulDiscoveryProperties发现默认queryPassing=false,通过配置改为true即可返回所有健康检查通过的服务。
spring cloud bus 分布式应用配置更新通知 Spring cloud bus支持rabbitmq和kafka,因为公司已经部署kafka,所以使用了kafka作为spring cloud bus的消息组件。
配置如下:
#spring cloud bus kafka spring.cloud.bus.enabled=true spring.cloud.bus.trace.enabled=false spring.cloud.stream.kafka.binder.zk-nodes=192.168.11.30:2181,192.168.11.33:2181,192.168.11.35:2181 spring.cloud.stream.kafka.binder.brokers=192.168.11.30:9092,192.168.11.33:9092,192.168.11.35:9092
架构如下: 这时Spring Cloud Bus做配置更新步骤如下:
spring boot admin是spring boot应用的监控管理服务,通过服务发现拉取服务列表,通过ApplicationRegistry注册到内部的map或者使用Hazelcast分布式存储。通过扩展zuul的RouteLocator对各个应用的spring boot actuator endpoint进行路由请求,返回各个应用的各项指标数据,通过angular js展现。地址: http://middleware.nidianwo.com/
界面如下: 可以通过界面查看应用的健康状况,内存磁盘使用,java gc情况,配置,日志,thread dump heap dump等各项应用数据。 界面如下:
通过日志的实时监控,可以方便观察应用启动状况,以及实时请求的日志,方便调试。
spring cloud netflix包含了很多非常有用的微服务组件,比如服务发现(Eureka),断路器(Hystrix),智能路由(Zuul)和客户端负载均衡(Ribbon),声明式服务调用client(feign)
feign的使用非常简单
应用main类增加注解@EnableFeignClients(basePackages = "com.dianwoba.open.express.service"),
添加服务调用接口类:
@FeignClient(name = "heimdall", url="${express.open.url}", configuration = RemoteConfiguration.class) public interface RemoteExpressService { //@Headers({"Content-Type: application/x-www-form-urlencoded","Accept: application/x-www-form-urlencoded"}) @RequestMapping(method = RequestMethod.POST, value = "/express/api/v1/addModifySite") @ResponseBody ApiResponse addModifySite(SiteDTO siteDTO); }
Feign内部通过几个组件进行请求的封装、调用和响应的解析。Contract实现上面接口方法的注解的解析以及请求的包装,Encoder实现上面接口方法参数的解析以及请求的组装,Feign.Builder实现核心类feign对象的建造器,Decoder实现响应的解析生成返回对象,Logger实现日志记录方式。这些接口都有默认实现,通过一个配置类配置了默认实现,默认实现为:
程序实现过程如下:
zuul作为微服务网关,具有动态路由和过滤器链功能,基于zuul可以实现:
o 认证&鉴权
o 数据统计
o 服务路由
o 协助单点压测
o 限流
o 静态响应
Zuul通过ZuulhanderMapping实现了SrpingMVC的AbstractUrlHandlerMapping,通过RouteLocator获取Route列表,映射对应route的fullPath到ZuulController,ZuulController继承了ServletWrappingController,会把对应请求代理到ZuulServlet,而ZuulServlet是zuul过滤器链的入口,过滤器链分三种类型,分别是pre、route、post分别对应请求执行前,请求路由执行,请求执行返回的操作,可以自定义各种过滤器实现特定需求。
使用非常简单
spring cloud 微服务组件使用感觉比较简单方便,通常是增加starter依赖,开启开关配置,启动类开启注解就能使用,使用过程中可能会遇到一些小问题,不过spring boot以及spring cloud社区非常活跃,版本迭代非常快,问题应该比较好解决,自己也可以看看源码通过解决问题来学习相关设计思想。