原来阿里华为等大厂都是这么设计微服务接口的!(中)

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 第一,针对响应体的设计混乱、响应结果的不明确问题,服务端需要明确响应体每一个字段的意义,以一致的方式进行处理,并确保不透传下游服务的错误。第二,针对接口版本控制问题,主要就是在开发接口之前明确版本控制策略,以及尽量使用统一的版本控制策略两方面。第三,针对接口的处理方式,我认为需要明确要么是同步要么是异步。如果API列表中既有同步接口也有异步接口,那么最好直接在接口名中明确。

明确接口的设计逻辑,即可实现下单服务的服务端和客户端来模拟

首先,实现服务端的逻辑:

image.png

客户端按流程图逻辑实现,模拟下单场景:


error==1 模拟一个不存在的URL,请求无法到收单服务,会得到404的HTTP状态码,直接进行友好提示,这是第一层处理

error==2 模拟userId参数为空,下单服务因缺少userId参数提示非法用户,把响应体中的message展示给用户

error3 模拟userId1,因为用户有风险,下单服务调用订单服务出错。处理方式和之前没有任何区别,因为下单服务会屏蔽订单服务的内部错误。


但在服务端可以看到如下错误信息:

[WARN ] [.c.a.d.APIThreeLevelStatusController:36  ] - 用户 1 调用订单服务失败,原因是 Risk order detected

error==0的用例模拟正常用户,下单成功。这时可以解析data结构体提取业务结果,作为兜底,需要判断订单状态,如果不是Created则给予友好提示,否则查询orderId获得下单的订单号,这是第三层处理。

客户端的实现代码如下:

@GetMapping("client")
public String client(@RequestParam(value = "error", defaultValue = "0") int error) {
   String url = Arrays.asList("http://localhost:45678/apiresposne/server?userId=2",
        "http://localhost:45678/apiresposne/server2",
        "http://localhost:45678/apiresposne/server?userId=",
        "http://localhost:45678/apiresposne/server?userId=1").get(error);
    //第一层,先看状态码,如果状态码不是200,不处理响应体
    String response = "";
    try {
        response = Request.Get(url).execute().returnContent().asString();
    } catch (HttpResponseException e) {
        log.warn("请求服务端出现返回非200", e);
        return "服务器忙,请稍后再试!";
    } catch (IOException e) {
        e.printStackTrace();
    }
    //状态码为200的情况下处理响应体
    if (!response.equals("")) {
        try {
            APIResponse<OrderInfo> apiResponse = objectMapper.readValue(response, new TypeReference<APIResponse<OrderInfo>>() {
            });
            //第二层,success是false直接提示用户
            if (!apiResponse.isSuccess()) {
                return String.format("创建订单失败,请稍后再试,错误代码: %s 错误原因:%s", apiResponse.getCode(), apiResponse.getMessage());
            } else {
                //第三层,往下解析OrderInfo
                OrderInfo orderInfo = apiResponse.getData();
                if ("Created".equals(orderInfo.getStatus()))
                    return String.format("创建订单成功,订单号是:%s,状态是:%s", orderInfo.getOrderId(), orderInfo.getStatus());
                else
                    return String.format("创建订单失败,请联系客服处理");
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
    return "";
}

改造后代码明确了接口每一个字段的含义,以及对于各种情况服务端的输出和客户端的处理步骤,对齐了客户端和服务端的处理逻辑。那么现在,你能回答前面那4个让人疑惑的问题了吗?


为简化服务端代码,可把包装API响应体APIResponse的工作交由框架自动完成,这样直接返回DTO OrderInfo即可。对于业务逻辑错误,可以抛出一个自定义异常:

image.png

在ServerException中包含错误码和错误消息:

image.png

然后,定义一个 @RestControllerAdvice 完成自动包装响应体:


通过实现ResponseBodyAdvice接口的beforeBodyWrite 处理成功请求的响应体转换

实现一个 @ExceptionHandler 来处理业务异常时,ServerException到ServerResponse的转换。

image.png

实现一个 @NoAPIResponse 注解。某些 @RestController 接口不希望实现自动包装的话,可以标记这个注解:

image.png

在ResponseBodyAdvice#supports方法

此组件是否支持给定的controller方法返回类型和所选的HttpMessageConverter类型

image.png

我们排除了标记有该注解的方法或类的自动响应体包装。

对于刚才我们实现的测试客户端client方法无需包装为APIResponse,即可标记该注解

image.png

这样我们的业务逻辑中就不需要考虑响应体的包装,代码会更简洁。

考虑接口变迁的版本策略

接口不可能一成不变,需根据业务需求不断变化内部逻辑。

若是大功能调整或重构,涉及参数定义的变化或参数废弃,导致接口无法向前兼容,这时接口就需版本概念。

版本策略最好一开始就考虑

既然接口总是要变迁,最好一开始就确定版本策略。

比如有如下实现策略:

URL Path

@GetMapping("/v1/api/user")
public int right1(){
    return 1;
}

QueryString的version参数

@GetMapping(value = "/api/user", params = "version=2")
public int right2(@RequestParam("version") int version) {
    return 2;
}

请求头中的X-API-VERSION参数

@GetMapping(value = "/api/user", headers = "X-API-VERSION=3")
public int right3(@RequestHeader("X-API-VERSION") int version) {
    return 3;
}

客户端即可在配置中处理相关版本控制的参数,有可能实现版本的动态切换。

方案对比选型

  • URL Path最直观、最不易出错
  • QueryString不易携带,不太推荐作为公开API的版本策略
  • HTTP头较无侵入性,若仅仅是部分接口需要进行版本控制,可考虑
目录
相关文章
|
5月前
|
Java API 微服务
【Spring Boot系列】通过OpenAPI规范构建微服务服务接口
【4月更文挑战第5天】通过OpenAPI接口构建Spring Boot服务RestAPI接口
215 0
|
5月前
|
微服务
jeecg微服务项目调用接口报错Token验证失效的解决方法
jeecg微服务项目调用接口报错Token验证失效的解决方法
|
5月前
|
消息中间件 分布式计算 中间件
秀出天际!阿里甩出的988页分布式微服务架构进阶神仙手册我粉了
秀出天际!阿里甩出的988页分布式微服务架构进阶神仙手册我粉了
|
5月前
|
Java 数据库 索引
最强阿里及大厂350道面试大全:框架+数据库+并发+开源+微服务
无论是对于刚入行工作还是已经工作几年的java开发者来说,面试求职始终是你需要直面的一件事情。首先梳理自己的知识体系,针对性准备,会有事半功倍的效果。我们往往会把重点放在技术上,而忽略了人事部分,实际上人事面试也会影响到最终的结果,把每一个环节做好,最终的结果自然不会差。
|
5月前
|
Java Docker 微服务
阿里P8携手腾讯T4谈微服务架构实战:深入浅出Cloud+boot+Docker
微服务”架构在这几年被广泛传播,变得非常火热,以至于关于微服务架构相关的开源框架和工具都变得越来越活跃,比如: Netflix OSS. Dubbo. Apache Thrift等。Spring Cloud也因为Spring社区在企业应用领域的广泛知名度和强大影响力,受到了广大架构师与开发者的高度关注。
|
10月前
|
存储 弹性计算 运维
傻掉!看华为技术专家的500页微服务架构笔记,感觉我格局太小
未来10年是各行各业数字化转型的关键10年。数字化转型将帮助企业打破原有IT系统的烟囱状布局,解决IT应用数据孤岛问题,实现数据集中管理共享,从而为企业降低成本、提高运营效率、加快产品创新提供平台和技术保证,使企业在市场竞争中获得优势。
|
5月前
|
Dubbo 应用服务中间件 Docker
阿里P8架构师谈微服务架构:Dubbo+Docker+SpringBoot+Cloud
什么是微服务架构呢?简单说就是将一个完整的应用(单体应用) 按照一定的拆分规则(后文讲述)拆分成多个不同的服务,每个服务都能独立地进行开发、部署、扩展。服务于服务之间通过注入RESTful api或其他方式调用。
|
5月前
|
消息中间件 前端开发 架构师
华为架构师复盘2024最全2340页面试题jvm+spring+redis+MQ+微服务
包括 Java 集合、JVM、多线程、并发编程、设计模式、Spring全家桶、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、Python、HTML、CSS、Vue、React、JavaScript、Android 大数据、阿里巴巴等大厂面试题等、等技术栈!
|
5月前
|
负载均衡 Dubbo 应用服务中间件
阿里微服务架构到底多牛逼:深入解析Apache Dubbo与实战
在Apache Dubbo (以下简称Dubbo)重新开源之前,Dubbo已经被很多公司广泛用于生产环境并获得了良好的反馈,很多公司内部也会建立私有分支自己维护,其中Dubbox 就是基于Dubbo分支进行扩展并二次维护的。重新开源后,社区维护的Dubbo版本进行了大量“bug fix" .和特性支持,收到了大量Dubbo用户的支持和参与。编写本书的想法是在开源后提出来的,因此本书取名《深入理解Apache Dubbo与实战》。
|
22天前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2