【摘要】 本文介绍了在微服务架构持续演进过程中,如何解决多种REST开发框架并存的问题。重点描述了网关在将请求转发给不同框架的微服务实例时,治理能力的差异;描述了CSE在提供第三方微服务访问方面的治理能力增强和开发。

开发团队选择同样的开发框架能够更好的进行经验积累和知识共享,从而提高开发效率。在实际项目中,这个过程经常被打破。团队需要根据用户需求的变化,选择更好的开发框架来解决面对的新问题。每个开发团队都不得不采取“持续迭代演进”的方法,来改造旧系统,开发新系统。

在[单体应用微服务改造实践]( https://bbs.huaweicloud.com/blogs/17ad483f325f11e9bd5a7ca23e93a891)中,分享了一种单体应用“持续迭代”改造微服务的方法。本文结合在改造过程中多微服务框架并存问题,描述一下使用CSE的开发实践。

本文假设应用已经采用网关搭建可持续演进的架构,如下图。这个架构由多种开发框架构建的基于REST的微服务组成。

本文提供的示例项目代码托管在[github](https://github.com/huaweicse/cse-java-chassis-samples/tree/master/multi-framework) 。

1      网关转发规则开发

CSE提供的edge service默认提供的转发规则对于采用CSE开发的微服务最简单,功能也最强大。根据配置的URL规则,将请求转发到对应的微服务,只是网关核心功能之一。当edge service将请求转发到CSE的微服务的时候,还具备如下功能:

·实例发现和动态更新。网关可以通过微服务名称从服务中心发现实例列表,并在实例列表变化的时候,自动更新。

·实例隔离和重试。当微服务存在多个实例的时候,如果一个实例出现故障,那么可以进行一次重试,将请求转发到其他实例;如果一个实例多次出现故障,需要在后续请求中将该实例隔离一段时间,避免频繁失败。

·支持灰度发布。网关可以结合请求参数规则,或者版本规则,将请求转发到符合规则的实例上面去。比如将请求参数count>20的请求转发给v2版本的实例,count<=20的请求转发给v1版本的实例。

·支持AZ亲和。网关需要结合微服务部署的数据中心信息,将请求转发给就近的数据中心,以提高效率。

·支持流量控制。可以针对微服务、微服务的某个接口进行流量控制。

·自动按照接口兼容性转发。这个场景指的是如果微服务存在多个版本,比如v1存在接口/a/b/c,v2新增了接口/x/y/z,那么当用户请求/x/y/z的时候,能够自动将请求转发给v2,而不需要做任何配置。

·支持故障注入。故障注入用于模拟故障,这个在开发测试阶段比较有用。通过模拟延时等情况,能够测试网关在异常情况下是否正常工作。

上面列的功能是edge service的部分功能,这些功能都是开箱即用,无需用户开发的。此外,CSE提供handler的机制,还实现了其他大量的开箱即用功能,在上述列表中没有给出。下面的例子实现了一个edge service转发给CSE服务的Dispatcher,CSE也默认提供了几个Dispatcher,本例子参考了默认的Dispatcher代码,定制了转发的URL。

public class DefaultCseDispatcher extends AbstractEdgeDispatcher {@Overridepublic void init(Router router) {// Dispatcher patterns. This dispatcher only forward requests to store.router.routeWithRegex("/store/(.*)").handler(CookieHandler.create());router.routeWithRegex("/store/(.*)").handler(createBodyHandler());router.routeWithRegex("/store/(.*)").failureHandler(this::onFailure).handler(this::onRequest);}protected void onRequest(RoutingContext context) {Map<String, String> pathParams = context.pathParams();String microserviceName = "store";String path = "/" + pathParams.get("param0");EdgeInvocation edgeInvocation = new EdgeInvocation();edgeInvocation.init(microserviceName, context, path, httpServerFilters);edgeInvocation.edgeInvoke();}@Overridepublic int getOrder() {return 10000;}
}

Edge service可以通过Dispatcher扩展,将请求转发给非CSE开发的REST服务。并且提供了一些扩展API供开发者使用,使用这些扩展API,能够使用CSE提供的部分功能。

其中:

·DiscoveryTree 集成了实例发现和动态更新、实例隔离、AZ亲和等功能。

·LoadbalanceHandler 集成了重试、灰度发布等功能。

下面例子展示了将请求转发给auth服务。

public class GenericHttpWithDiscoveryDispatcher extends AbstractEdgeDispatcher {private static Logger LOGGER = LoggerFactory.getLogger(GenericHttpWithDiscoveryDispatcher.class);private static Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null);private static HttpClient httpClient = vertx.createHttpClient(new HttpClientOptions());private DiscoveryTree discoveryTree = new DiscoveryTree();private LoadbalanceHandler loadbalanceHandler;class RetriableHandler implements Handler {private RoutingContext context;private String path;private Buffer data;private boolean isRetry = false;public RetriableHandler(RoutingContext context, String path) {this.context = context;this.path = path;this.context.response().setChunked(true);}@Overridepublic void handle(Invocation invocation, AsyncResponse asyncResponse) throws Exception {URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress();HttpClientRequest clietRequest =httpClient.request(context.request().method(),endpoint.getPort(),endpoint.getHostOrIp(),"/" + path + "?" + context.request().query(),clientResponse -> {context.response().setStatusCode(clientResponse.statusCode());VertxHttpHeaders headers = new VertxHttpHeaders();clientResponse.headers().forEach(entry -> {headers.add(entry.getKey(), entry.getValue());});context.response().headers().setAll(headers);clientResponse.handler(data -> {context.response().write(data);});clientResponse.endHandler((v) -> {context.response().end();asyncResponse.success("OK");});});clietRequest.headers().setAll(context.request().headers());clietRequest.exceptionHandler(e -> {asyncResponse.consumerFail(e);});if (!isRetry) {// data can not be read twice, so cache it in retry this.context.request().handler(d -> {clietRequest.write(d);data = d;});context.request().endHandler((v) -> {clietRequest.end();});isRetry = true;} else {if (data != null) {clietRequest.write(data);}clietRequest.end();}}}public GenericHttpWithDiscoveryDispatcher() {discoveryTree.addFilter(new IsolationDiscoveryFilter());discoveryTree.addFilter(new ServerDiscoveryFilter());discoveryTree.sort();loadbalanceHandler = new LoadbalanceHandler(discoveryTree);}@Overridepublic int getOrder() {return 10001;}@Overridepublic void init(Router router) {// Dispatcher patterns. This dispatcher only forward requests to auth.String regex = "/auth/(.*)";router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest);}protected void onRequest(RoutingContext context) {Map<String, String> pathParams = context.pathParams();String microserviceName = "auth";String path = pathParams.get("param0");Invocation invocation =new NonSwaggerInvocation(RegistryUtils.getAppId(), microserviceName, "0+", new RetriableHandler(context, path));try {loadbalanceHandler.handle(invocation, resp -> {if (resp.isFailed()) {context.response().setStatusCode(resp.getStatusCode());context.response().write(((Exception) resp.getResult()).getMessage());context.response().end();}});} catch (Exception e) {LOGGER.error("", e);}}}

由于auth服务采用Spring MVC开发,该服务在注册的时候,不会生成契约信息。因此上面列出的几个功能是不具备的:

·不支持流量控制。

·不支持自动按照接口兼容性转发。

·不支持故障注入。

此外,CSE通过Handler扩展提供的其他开箱即用功能,也是不具备的。

最后一个服务就是将请求转发给user了。user服务没有向服务中心注册,因此没有微服务信息,也没有契约信息。需要在代码里面写上实例列表和自己定义负载均衡。

public class GenericHttpDispatcher extends AbstractEdgeDispatcher {private static Logger LOGGER = LoggerFactory.getLogger(GenericHttpDispatcher.class);private static Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null);private static HttpClient httpClient = vertx.createHttpClient(new HttpClientOptions());public GenericHttpDispatcher() {}@Overridepublic int getOrder() {return 10001;}@Overridepublic void init(Router router) {// Dispatcher patterns. This dispatcher only forward requests to user.String regex = "/user/(.*)";router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest);}protected void onRequest(RoutingContext context) {Map<String, String> pathParams = context.pathParams();String path = pathParams.get("param0");HttpClientRequest clietRequest =httpClient.request(context.request().method(),// hard coded ip/port here. can use configurations. 9093,"localhost","/" + path + "?" + context.request().query(),clientResponse -> {context.response().setStatusCode(clientResponse.statusCode());VertxHttpHeaders headers = new VertxHttpHeaders();clientResponse.headers().forEach(entry -> {headers.add(entry.getKey(), entry.getValue());});context.response().headers().setAll(headers);clientResponse.handler(data -> {context.response().write(data);});clientResponse.endHandler((v) -> {context.response().end();});});clietRequest.headers().setAll(context.request().headers());clietRequest.exceptionHandler(e -> {LOGGER.error("", e);});context.request().handler(data -> {clietRequest.write(data);});context.request().endHandler((v) -> {clietRequest.end(); });}
}

网关给user服务转发的过程中,没有上面列举的所有服务治理能力,只是一个单纯的HTTP转发器。

2      CSE访问第三方服务

在edge service转发给CSE服务的过程中,介绍了一组治理能力。这组治理能力在CSE服务调用其他CSE的服务的时候,也是具备的,而且这组能力不仅体现在调用者(consumer),提供者(provider)处理请求的时候,也会走handler链,从而具备流控、隔离等能力。 CSE之间的微服务调用非常简单,包括RestTemplate和RPC两种开发模式,这里不详细介绍,开发者可以参考开发指南进行学习。

CSE也提供了调用第三方服务的能力,通过使用CSE提供的第三方能力,这些治理能力对于调用者(consumer)一端,也是全部具备的。因此在CSE服务中调用其他第三方服务的时候,应该尽可能使用CSE提供的第三方访问能力,而不是使用普通的HTTP client。

使用CSE的http client的原理是首选通过接口的方式声明需要访问的服务端的契约。

@RequestMapping(path = "/")
public interface AuthService {@GetMapping(path = "/oauth/token")Token auth(@RequestParam(name = "username") String username, @RequestParam(name = "password") String password,@RequestParam(name = "grant_type") String grant_type,@RequestParam(name = "scope") String scope,@RequestParam(name = "client_id") String clientId,@RequestParam(name = "client_secret") String clientSecret);
}

然后本地模拟注册一个在服务中心注册的实例。

@Component
public class ThirdPartyRegistry implements BootListener {@Overridepublic void onBootEvent(BootEvent event) {if (event.getEventType() == EventType.AFTER_REGISTRY) {DiscoveryTree discoveryTree = new DiscoveryTree();discoveryTree.addFilter(new IsolationDiscoveryFilter());discoveryTree.addFilter(new ServerDiscoveryFilter());discoveryTree.sort();DiscoveryContext context = new DiscoveryContext();Invocation invocation =new NonSwaggerInvocation("default", "auth", "0+", null);context.setInputParameters(invocation);VersionedCache serversVersionedCache = discoveryTree.discovery(context,"default","auth","0+");List<ServiceCombServer> servers = serversVersionedCache.data();List<MicroserviceInstance> instances = new ArrayList<>(servers.size());servers.forEach(item -> {MicroserviceInstance instance = new MicroserviceInstance();List<String> endpoints = item.getInstance().getEndpoints();List<String> endpointsWithoutPrefix = new ArrayList<>(endpoints.size());endpoints.forEach(e -> {System.out.println(e);endpointsWithoutPrefix.add(e.substring(0, e.indexOf("?")));});instance.setEndpoints(endpointsWithoutPrefix);instances.add(instance);});RegistryUtils.getServiceRegistry().registerMicroserviceMapping("authStub","1.1.1",instances,AuthService.class);}}
}

最后就可以按照CSE访问CSE服务一样访问这个第三方服务。

  @RpcReference(microserviceName = "authStub", schemaId="authStub")private AuthService authService;@GetMapping("/auth")public Token auth() {return authService.auth("user_1", "123456", "password", "read", "client_2", "123456");}

3      第三方服务访问CSE

第三方访问CSE可以按照原生的第三方HTTP Client API来访问。 这个治理能力等同于第三方提供的功能,通常不包含治理能力。 这里不在详细描述。

4      总结

通过上面的例子,看到了CSE在处理多种REST开发框架并存情况下,网关转发和服务之间调用需要采用的技术。这些技术在微服务持续演进过程中会经常用到。但是微服务演进的最终目的是选择一套技术来解决问题,应该避免架构选型的多样化,这无疑会增加学习和维护的成本。同时可以看到CSE在服务治理能力方面的优势,这些优势对于微服务的可靠运行提供了非常强大的保障,这个才是CSE作为一个微服务框架核心重要的部分。

来源:华为云社区原创  作者:liubao68

基于CSE的微服务工程实践-多微服务框架演进相关推荐

  1. 基于CSE的微服务工程实践-Native API先行

    [摘要] 本文介绍了先写Native API的实践经验,介绍了在Native API先行的情况下,如何由设计人员定义面向JAVA语言的SDK包. Open API 采用Swagger进行描述,能够灵活 ...

  2. Rest 微服务工程搭建02——微服务消费者订单Module模块 || @RequestBody 的重要作用

    restTemplate的使用 https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springfra ...

  3. Kratos技术系列|从Kratos设计看Go微服务工程实践

    导读 github.com/go-kratos/kratos(以下简称Kratos)是一套轻量级 Go 微服务框架,致力于提供完整的微服务研发体验,整合相关框架及周边工具后,微服务治理相关部分可对整体 ...

  4. Rest 微服务工程搭建01——微服务提供者Module模块

    建表SQL语句的书写 CREATE TABLE `payment` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`serial` var ...

  5. 微服务工程消费dubbo服务的配置

    1. 应用场景 微服务工程,按规范不做xml文件的配置,同时需要调用其他工程提供的dubbo服务 2. 接口引入方式 l Jar包引入方式 通过maven pom文件,引入其他工程的api jar包. ...

  6. SpringBoot集成gRPC微服务工程搭建实践

    前言 本文将使用Maven.gRPC.Protocol buffers.Docker.Envoy等工具构建一个简单微服务工程,笔者所使用的示例工程是以前写的一个Java后端工程,因为最近都在 学习微服 ...

  7. WeText项目:一个基于.NET实现的DDD、CQRS与微服务架构的演示案例

    最近出于工作需要,了解了一下微服务架构(Microservice Architecture,MSA).我经过两周业余时间的努力,凭着自己对微服务架构的理解,从无到有,基于.NET打造了一个演示微服务架 ...

  8. python 微服务框架 知乎_今日头条Go建千亿级微服务的实践

    原标题:今日头条Go建千亿级微服务的实践 原文作者:字节跳动技术团队 来源:知乎 小编有话说:如何寻找优质的学习资源是是否能够自学成功的前提要素,知乎作为一个流量比较大的问题和知识分享社区,在gola ...

  9. 中小型研发团队架构实践:微服务架构

    http://www.infoq.com/cn/articles/architecture-practice-06-microservice-architect 一.MSA 简介 1.1.MSA 是什 ...

最新文章

  1. vs2005格式化代码
  2. easyui treegrid php,easyUI TreeGrid
  3. ML之Xgboost:利用Xgboost模型对数据集(比马印第安人糖尿病)进行二分类预测(5年内是否患糖尿病)
  4. blp模型 上读下写_CreditX在线借贷欺诈检测框架BLP
  5. 【2017-04--28】Winform中ListView控件
  6. Apsara Stack 技术百科 | 浅谈阿里云混合云新一代运维平台演进与实践
  7. Ajax在请求数据时显示等待动画遮罩
  8. hexo没有样式_一款被大厂选用的 Hexo 博客主题
  9. 大数据集群跨多版本升级、业务0中断,只因背后有TA
  10. 接口做的好怎么形容_匠品美缝知识之接口美缝如何做?
  11. 《深入浅出DPDK》读书笔记(一):基础部分知识点
  12. nodeJS+bootstarp+mongodb整一个TODO小例子
  13. springboot调整请求头大小_新手也能看懂的 SpringBoot 异步编程指南
  14. apiCloud实现微信分享功能
  15. [LOJ2339][虚树][边分治][树形DP]WC2018:通道
  16. 视频教程-征服Node.js 7.x视频课程(6):文件系统与Stream视频课程-Node.js
  17. 使用vi编辑器编辑一个C语言源程序hello.c,并使用编译工具GCC编译该源程序。
  18. c语言笔试程序改错题,C语言笔试--程序改错题.doc
  19. Windows下的Node.js安装及环境配置
  20. 学习GestureDetectorCompat,实现卡片左右滑动消失效果

热门文章

  1. 用python二重循环求成绩表_python的循环
  2. python+pyqt5实现24点小游戏
  3. CJOJ 【DP合集】最长上升序列2 — LIS2
  4. Map 遍历的几种方法
  5. OpenDayLight Helium实验一 OpenDaylight的C/S模式实验
  6. js添加事件、移除事件、阻止冒泡、阻止浏览器默认行为等写法(兼容IE/FF/CHROME) 转载...
  7. matlab 径向偏振光,径向偏振光的产生与应用..docx
  8. rtt面向对象oopc——5.IO设备管理之快速查看设备父类调用设备子类的方法
  9. oopc——7.面向接口编程
  10. java web 请求跟踪_java web 项目跟踪用户操作