微服务架构技术调研<3>--微服务架构实践

引言:

由于公司商业上有实打实的需求和场景,倒逼产品开始思考架构升级,以适应这种商业环境的快速变化。架构师在进行技术选型或者架构升级前,需要做大量技术调研、竞品分析,《微服务架构综述》则是对服务化架构技术调研产生的调研报告,将会从如下三个角度分析微服务架构的“前生今世”,从而为产品团队的技术转型找到一些理论依据:

  1. 什么是微服务架构,其与传统架构区别联系
  2. 为什么要向微服务架构转型
  3. 微服务架构实践

#微服务架构实践

##选择框架的标准[8]

微服务有着多种框架可供挑选,在挑选合适的框架时,可以把以下几点作为参考的指标:

  • 流行程度——这一点可以根据行业对框架的接受度来衡量,具体的量化指标就是将框架作为企业标准的用户数量。其他可供参考的指标还有有关框架文档的可用性,以及市场上可用的熟练资源的数量。
  • 社区的成熟度——这一标准可以用这两点来衡量:框架在社区/商业支持方面的成熟度,修复问题和增添新功能的频率。
  • 开发难度——支持框架的 IDE 和工具在快速应用程序开发中也发挥着重要作用。
  • 学习曲线——提供各种形式的文档可以大大提升学习效率,以及开发人员的生产力,例如教程、最佳实践、典型问题的解决方案等
  • 架构支持——框架提供代码模块和接口,内置设计模式降低应用程序开发人员的编码复杂性。
  • 自动化支持 ——框架支持自动化与构建和部署微服务相关的任务
  • 独立部署 ——框架必须支持独立部署的两个方面——向上兼容、向下兼容、可重用性和可移植性
  • 持续集成 ——开发人员经常将代码集成到共享存储库中,通过自动化构建和自动化测试框架来支持每个集成。

##开源微服务方案[8]

 

注:对微服务架构师来说,截止目前Spring Cloud和Dubbo体系是最完善的开源方案。dubbo现已融入spring-cloud-alibaba项目

##spring cloud方案[11]

### spring、spring boot与spring cloud

spring

Spring框架为开发Java应用程序提供了全面的基础架构支持。它包含一些很好的功能,如依赖注入和开箱即用的模块,如:Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP、Spring ORM 、Spring Test,这些模块缩短应用程序的开发时间,提高了应用开发的效率例如

spring boot

Spring Boot基本上是Spring框架的扩展,主要是简化了大量的配置工作。以约定优于配置、开箱即用策略,提供了一个可以快速搭建应用的框架,它消除了设置Spring应用程序所需的XML配置(改用注解),为更快,更高效的开发生态系统铺平了道路。

Spring不同,Spring Boot只需要一个依赖项来启动和运行Web应用程序

spring cloud

SpringCloud, 基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。

  • SpringBoot专注于快速方便的开发单个个体微服务。
  • SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等集成服务。

###服务注册与发现方案

spring作为一种抽象级别很高的框架,底层自然支持多种具体的服务发现实现技术的。

####Eureka方案

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。

SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。

 

Eureka包含两个组件:

  • EurakaServer

负责注册服务,各个节点启动后,注册,服务注册表中会有存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到

spring引入euraka server组件


 配置euraka服务

注册中心服务端主要对外提供了四个个功能:

服务注册:服务提供者启动时,会通过 Eureka Client 向 Eureka Server 注册信息,Eureka Server 会存储该服务的信息,Eureka Server 内部有二层缓存机制来维护整个注册表

提供注册表:服务消费者在调用服务时,如果 Eureka Client 没有缓存注册表的话,会从 Eureka Server 获取最新的注册表

同步状态:Eureka Client 通过注册、心跳机制和 Eureka Server 同步当前客户端的状态。

服务剔除:当 Eureka Client 和 Eureka Server 不再有心跳时,Eureka Server 会将该服务实例从服务注册列表中删除,即服务剔除。

  • EurekaClinet

是java客户端,用于简化Eureka Server的操作,客户端同时内置使轮询负载算法的负载均衡器,在启动后,会三十秒,向服务器发送心跳,如果服务器有多个心跳周期内没有接受到某个节点的心跳,那么服务器就会在注册表中移除节点,默认周期为90秒

spring引入eureka客户端组件

eureka客户端配置,指定server端地址、端口、心跳间隔等

 

客户端致提供两个功能:

Register: 服务注册,服务的提供者,将自身注册到注册中心,服务提供者也是一个 Eureka Client。当 Eureka Client 向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。

Renew: 服务续约,Eureka Client 会每隔 30 秒发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka Client 运行正常,没有出现问题。 默认情况下,如果 Eureka Server 在 90 秒内没有收到 Eureka Client 的续约,Server 端会将实例从其注册表中删除,此时间可配置,一般情况不建议更改。

Eureka 服务管理界面:

Eureka集群实现高可用:

 

####Consul方案

spring没有提供内嵌consul服务端的组件,因此需要使用者自行启动consul服务器

spring引入consul客户端组件

客户端地址、端口、服务注册名、心跳等配置

 

 consul服务管理界面:

####微服务间API调用 

Spring框架提供RestTemplate 类来实现rest API调用

服务端代码示例:

@GetMapping("/order")

public String getOrder(){

        RestTemplate restTemplate = new RestTemplate();

        String res =restTemplate.getForObject("http://localhost:8888/getUser",String.class);

        System.out.println("res = " + res);

        return "getOrder" + " ====  " + res;

}

客户端代码示例:

# GET 方法调用

Map<String, String> vars = Collections.singletonMap("hotel", "42");

String result = restTemplate.getForObject("http://127.0.0.1:8080/xxx", String.class, vars);

# POST 方法调用

JSONObject param = new JSONObject();

ResponseEntity<JSONObject> responseEntity=restTemplate.postForEntity("http://127.0.0.1:8080/xxx",params,JSONObject.class);

int statusCodeValue = responseEntity.getStatusCodeValue();

HttpHeaders headers = responseEntity.getHeaders();

JSONObject body = responseEntity.getBody();

API调用带来的问题:

  • 调用服务的路径主机和服务端直接写死在url中无法实现服务集群时请求负载均衡
  • 调用服务的请求路径写死在代码中,日后服务路径、地址等发生变化时不利于后期维护

解决方案——负载均衡

  • 使用自定义负载均衡(自定义随机)—— 无法对服务进行健康检查
  • 使用Ribbon负载均衡

###负载均衡

为什么需要负载均衡,微服务集群场景,用于流量消峰、便于集群无感知横向扩容

####Ribbon方案

spring框架集成的ribbon组件,通过对ribbon的高度封装,实现负载均衡功能

在这里进行处理,提供三种使用方式:

  • DiscoveryClient:服务发现客户端对象,可以根据服务ID进行获取对应的服务列表
  • LoadBalancerClient:负载均衡客户端对象,同样的根据ID获取列表,之后根据内部负载均衡机制策略选择其中的一个服务
  • @LoadBalanced:负载均衡注解,作用同上
// DiscoveryClient方式
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("Users");
serviceInstances.forEach(serviceInstance -> {
	System.out.println("host = "+ serviceInstance.getHost() +", port ="+ serviceInstance.getPort() +", url ="+ serviceInstance.getUri());
});

// LoadBalancer方式
ServiceInstance serviceInstances1 = loadBalancerClient.choose("Users");
System.out.println("url = " + serviceInstances1.getUri()+", port = "+serviceInstances1.getPort() + " ,host = "+serviceInstances1.getHost());

// 负载均衡注解方式
@Configuration
public class BeanConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

 ribbon实现负载均衡的原理

 总结:ribbon实现的关键点是为ribbon定制的RestTemplate,ribbon利用了RestTemplate的拦截器机制,在拦截器中实现ribbon的负载均衡。负载均衡的基本实现就是利用applicationName从服务注册中心获取可用的服务地址列表,然后通过一定算法负载,决定使用哪一个服务地址来进行http调用

####OpenFeign方案

自己百度吧,功能方面类似ribbon,但比ribbon更强大,可调试性也更佳。

###熔断机制

####为什么需要服务熔断

微服务架构中,服务间通过网络和API进行通信。和单机架构不同的是,分布式架构中服务的生产者和消费者之间由于进程隔离、网络隔离,生产者可能挂死、可能掉线、网络可能波动,因此服务能否顺利完成始终处于一种不确定状态。特别是在微服务架构中,服务间拆分粒度更细,调用链变长,加剧了这种不确定性。

服务雪崩场景:

服务A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算服务A能扛得住请求,服务B和服务C未必能扛得住这突发的请求。此时,如果服务C因为抗不住请求,变得不可用。那么服务B的请求时也会阻塞(在等待超时、重试等机制下),慢慢耗尽服务B的线程资源,服务B就会变得不可用。紧接着,服务A也会不可用,这一过程如下图所示:

解决方案——服务熔断:

下游的服务因为某种原因突然变得不可用或响应过慢上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用

  • 开启熔断

在固定时间窗口内,接口调用超时比率达到一个阈值,会开启熔断。

进入熔断状态后,后续对该服务接口的调用不再经过网络,直接执行本地的默认方法,达到服务降级的效果。

  • 熔断恢复

熔断不可能是永久的。当经过了规定时间之后,服务将从熔断状态回复过来,再次接受调用方的远程调用。

####Hystrix方案

在spring项目中使用Hystrix组件实现熔断,参考:[12]

熔断流程:

在服务消费者定义Hystrix熔断器。当Hystrix监控到对该服务接口调用触发两个阏值时,会在系统中自动触发熔断器,在熔断器打开期间内,任何到该接口请求均不可用,同时在断路器打开5s后断路器会处于半开状态,此时断路器允许放行一个请求到该服务接口,如果该请求执行成功,断路器彻底关闭,如果该请求执行失败断路器重新打开。

服务降级:

客户端从整体网站请求负载考虑,当某个服务熔断或者关闭之后,服务将不再被调用此时在客户端,我们可以准备一个FallbackFactory,返回一个默认的值(缺省值),整体的服务水平下降了~ 但是好歹可用

Hystrix Dashboard仪表盘:

监控每一个@HystrixCommond注解创建一组度量,构建一组信息,然后通过图形化方式展示当前方法@Hystrixcomnond的状态信息

###服务网关

####什么是服务网关

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。

####服务网关的作用

  • 网关统一所有微服务入口
  • 网关可以实现请求路由转发(router dispatcher)以及请求过程负载均街
  • 访问服务的身份认证
  • 防报文重放与防数据算改
  • 功他调用的业务答.
  • 响应数据的脱敏
  • 流量与并发控制,甚至基于冲工调用的计量或者计费等等

####Gatway方案

Spring gatway组件实现什么功能:

  • API请求断言,断言不通过的请求按规则驳回
    • 基于DateTime
    • 基于远程地址
    • 基于Cookie
    • 基于请求头Header
    • 基于Method请求方法
    • 基于Path请求路径
    • 基于Query请求参数
  • 请求过滤器,过滤器就是在请求的传递过程中,对请求和响应做一些修改

引入spring gatway组件

gatway配置:

 

###配置中心

比如说有N个微服务的application.yml是相同的,我们要是想修改application.yml需要修改N个这时候我们就需要ConfigServer来帮我们解决这个问题,在配置中心修改一次众多个微服务生效。 SpringCloud Config为服务器架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。

配置也分为配置服务端与配置客户端

####Config Server配置服务器

首先在configServer当中会集成Git,我们可以将对应的配置文件放到远程仓库进行管理,这是因为在修改配置的时候Git会进行版本记录,方便后期版本回退,

在获取git仓库之后会加到本地缓存,这是为了避免当远程仓库宕机之后,本地微服务还是要得到统一配置。 

spring引入config server组件

 

 配置远端仓库

 

 

 之后我们直接通过地址进行拉取文件:http://localhost:8880/configclient-xxx.properties

 

 ####Config Client配置客户端

spring引入config client组件

在前面我们通过了ConfigServer获取到了远程仓库的配置文件,但是在client当中又该如何获取到呢?首先服务都注册到了注册中心,只需要获取到ConfigServer的服务id之后再去找对应的远程仓库即可,而在远程仓库当中又有很多的配置文件,在这里还需要指定获取到哪个配置文件。本地配置文件如下:

 

###消息中间件

Spring cloud bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring bus的一个核心思想是通过分布式的启动器对spring boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。

大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。

MQ可以起到大流量消峰、高并发异步处理的作用

还可以在服务生产者、消费者间解耦,避免直接API调用

AMQP模型:

以注册场景举例,MQ的使用场景[4]

 

由于发送邮件非必要业务,可以不同步执行,放入消息队列之后,由MQ根据订阅推送给消费者,这样可以达到分摊主程序性能、流量的目的,并且减少主程序的调用链

阿里RocketMQ[4]

###认证授权

####外部无状态,内部有状态”方案[14]

客户端传递JSESSIONID和Token给服务器端,服务器端拿到了Token确还使用session来认证授权,这是为什么呢?因为有些老的项目刚开始是用session来认证授权的,但是随着技术的更新,想要将session这种有状态方式转变为Token的无状态方式,但是重构又不是一蹴而就的,所以造成了有些子项目使用无状态方式,有些子项目又使用有状态方式;

####网关认证授权,内部裸奔”方案[14]

用户的登陆授权全部都在网关完成,网关接收到Token之后,就将Token解密解析出对应的用户信息,比兔下图的user_id和username,然后在将解密解析后的信息传递给微服务,微服务拿到用户信息之后就开始自身的业务逻辑,这里有一个弊端,微服务只能完全的信任网关,如果网关被攻破,那就有点尴尬了;当然也有优点,实现比较简单,性能较好;

 

 

####内部裸奔”改进方案[14]

该方案是上个方案的改进版,网关不再进行认证和授权操作,也不进行解密解析Token操作,只用作单纯的转发,用户的认证授权、解密解析Token全都交给微服务来实现,这样一来项目就更加的安全,性能和上个方案对比也不差;

####Spring Cloud Security“处处安全”方案[13] 

 

在使用 Spring Cloud 体系来构建微服务的过程中,用户请求是通过网关(ZUUL 或 Spring APIGateway)以 HTTP 协议来传输信息,API 网关将自己注册为 Eureka 服务治理下的应用,同时也从 Eureka 服务中获取所有其他微服务的实例信息。搭建 OAuth2 认证授权服务,并不是给每个微服务调用,而是通过 API 网关进行统一调用来对网关后的微服务做前置过滤,所有的请求都必须先通过 API 网关,API 网关在进行路由转发之前对该请求进行前置校验,实现对微服务系统中的其他的服务接口的安全与权限校验。对于微服务安全认证授权机制一块,目前主流的解决方案有 OAuth2.0 与 OIDC(OpenID Connect) 等标准协议。

OAuth2.0 授权模式

注:在OAuth2.0标准中,不同角色间的请求跳转都采用标准http重定向

完整授权流程中有四个重要的角色[ RFC 6749 ]:

  • 资源拥有者(resource owner):能授权访问受保护资源的一个实体,可以是一个人,那我们称之为最终用户;
  • 资源服务器(resource server):存储受保护资源,客户端通过access token请求资源,资源服务器响应受保护资源给客户端;
  • 授权服务器(authorization server):成功验证资源拥有者并获取授权之后,授权服务器颁发授权令牌(Access Token)给客户端。
  • 客户端(client:第三方应用,也可以是它自己的官方应用;其本身不存储资源,而是资源拥有者授权通过后,使用它的授权(授权令牌)访问受保护资源,然后客户端把相应的数据展示出来/提交到服务器。

####四种方案对比[14]

###监控中心

由于微服务架构下,各个子系统拆分粒度较细,且系统架构设计复杂。大大提高了系统容错、排错、运维难度。

未解决此痛点,统一的监控中心是必不可少的方案。

常用的监控数据包括:

  • 注册微服务监控(性能、在线离线状态、负载等)
  • API监控
  • 调用链监控(分布式服务跟踪)
  • 熔断监控
  • 微服务容器与宿主机的监控
  • 统一配置管理
  • 日志与异常监控

参考:

[1] Spring Cloud Alibaba 新一代微服务解决方案

[2] Martin Fowler个人主页微服务文章

[3] 阿里云微服务方案

[4] 《人人都是架构师》

[5] 齐商银行:行级分布式微服务系统架构

[6] 微服务架构优缺点

[7] Contino是一家英国非上市科技咨询服务商,Contino帮助客户加速采用云原生技术

[8] 知乎-微服务框架有哪些?

[9] 知乎-彻底搞清楚!SOA和微服务的区别

[10] 知乎-SOA和微服务架构的区别?

[11] CSDN-Spring Cloud 微服务解决方案 详解

[12] SpringCloud之熔断器Hystrix

[13] Spring Cloud 微服务中搭建 OAuth2.0 认证授权服务

[14] 微服务的用户认证与授权

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值