转载

spring cloud微服务实践

背景

随着微服务的流行,基于spring生态的spring cloud越来越被关注, Spring Cloud专注于提供良好的开箱即用的典型用例和可扩展性覆盖机制。

特性

  • 分布式/版本化配置
  • 服务注册和发现
  • 路由
  • service - to - service调用
  • 负载均衡
  • 断路器

Spring cloud包含两个基本模块,spring cloud context和spring cloud commons。

spring cloud context即spring cloud应用上下文,包含引导上下文(加载bootstrap配置)、配置加密、配置刷新范围(RefreshScope)、控制端点(/env/reset,/refresh,/restart等)功能。

Spring cloud commons 提供服务注册发现,负载均衡,断路器等模式的一个共用抽象层,为具体现实提供统一抽象。

实际应用过程中,我们使用了

  • spring cloud consul作为服务注册和发现组件
  • spring cloud config作为分布式/版本化配置管理
  • spring cloud bus作为消息总线用于刷新分布式应用配置
  • spring boot admin作为统一的应用监控后台
  • spring cloud Netflix feign 作为rest服务调用client
  • spring cloud Netflix zuul 作为路由、过滤网关

spring cloud consul

Consul 是使用Go开发的开源的服务发现和健康检查的工具.Consul还提供K/V存储.可以很方便的使用Consul集群搭建高可用的微服务基础架构. Consul还包含web ui 可以查看各服务状态.

线上consul地址: http://consul.nidianwo.com/

如下截图: spring cloud微服务实践

因为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

Spring Cloud Config提供服务端和客户端在分布式系统中扩展配置。支持不同环境的配置(开发、测试、生产)。使用Git做默认配置后端,可支持配置环境打版本标签。

spring cloud config架构图如下:

spring cloud微服务实践

Spring cloud config包含4部分

  • git配置仓库,存放应用各环境配置
  • config server提供了基于资源的HTTP接口 通过@EnableConfigServer开启配置服务端应用
# 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方式提供配置资源,如下所示:

spring cloud微服务实践
  • config client,通过配置的url或者服务发现拉取config server配置 Config server 开启服务发现后,config client可以通过服务发现找到config server

我们的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微服务实践 这时Spring Cloud Bus做配置更新步骤如下:

  • 提交代码,利用 git 的 webhook 触发post请求给 bus/refresh
  • server端接收到请求并发送给Spring Cloud Bus
  • Spring Cloud bus接到消息并通知给其它客户端
  • 其它客户端接收到通知,请求Server端获取最新配置
  • 全部客户端均获取到最新的配置

spring boot admin

spring boot admin是spring boot应用的监控管理服务,通过服务发现拉取服务列表,通过ApplicationRegistry注册到内部的map或者使用Hazelcast分布式存储。通过扩展zuul的RouteLocator对各个应用的spring boot actuator endpoint进行路由请求,返回各个应用的各项指标数据,通过angular js展现。地址: http://middleware.nidianwo.com/

界面如下: spring cloud微服务实践 可以通过界面查看应用的健康状况,内存磁盘使用,java gc情况,配置,日志,thread dump heap dump等各项应用数据。 界面如下: spring cloud微服务实践

通过日志的实时监控,可以方便观察应用启动状况,以及实时请求的日志,方便调试。

spring cloud netfix feign

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实现日志记录方式。这些接口都有默认实现,通过一个配置类配置了默认实现,默认实现为:

  • Decoder feignDecoder: ResponseEntityDecoder(这是对SpringDecoder的封装)
  • Encoder feignEncoder: SpringEncoder
  • Logger feignLogger: Slf4jLogger
  • Contract feignContract: SpringMvcContract
  • Feign.Builder feignBuilder: HystrixFeign.Builder

程序实现过程如下:

  • 首先通过@EnableFeignCleints注解开启FeignCleint
  • 根据Feign的规则实现接口,并加@FeignCleint注解
  • 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。
  • 当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate
  • RequesTemplate在生成Request
  • Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
  • 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

spring cloud netfix zuul

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 boot启动类增加注解@EnableZuulProxy开启zuul代理功能。
  • 配置 zuul.routes.express.path=/express/**
    zuul.routes.express.serviceId=express-open-api
    zuul.routes.express.stripPrefix=false
    请求的不同path会路由到不同的服务。

一些体会

spring cloud 微服务组件使用感觉比较简单方便,通常是增加starter依赖,开启开关配置,启动类开启注解就能使用,使用过程中可能会遇到一些小问题,不过spring boot以及spring cloud社区非常活跃,版本迭代非常快,问题应该比较好解决,自己也可以看看源码通过解决问题来学习相关设计思想。

原文  http://tech.dianwoda.com/2017/09/04/spring-cloudxiang-guan-zu-jian-shi-jian/
正文到此结束
Loading...