.netcore 3.1高性能微服务架构:webapi规范

原文:.netcore 3.1高性能微服务架构:webapi规范

1.1 定义

1、基础接口:单一职责原则,每个接口只负责各自的业务,下接db,通用性强。

2、聚合接口:根据调用方需求聚合基础接口数据,业务性强。

1.2 协议

1. 客户端在通过 API 与后端服务通信的过程中, 应该使用 HTTPS(生产环境) 协议

2. 服务端响应的数据格式统一为JSON

1.3域名host

prd环境:https://xxx-xxx-api.example.com/

uat环境:https://xxx-xxx-api-uat.example.com/

test环境:https://xxx-xxx-api-test.example.com/

dev环境:https://xxx-xxx-api-dev.example.com/

将api放到子域名里,这种做法可以保持某些规模化上的灵活性。

1.4路径path

path命名应该是以资源为导向的命名,对资源的操作是由HttpMethod(get、post、put、delete)来决定。所以一般来说url上的单词都应该是名词,一定不要是动词。一般遵循以下约定:

(1)URL 的命名必须全部小写;

(2) URL 必须 是易读的 URL;

(3)一定不可 暴露服务器架构

(4)出现复合词汇使用下划线分隔,例如:animal_types

举几个正面例子:

新增用户:http://localhost/user post方法提交;

修改用户:http://localhost/users put方法提交;

删除文章:http://localhost/articles?author=1&category=2
delete方法提交;

查询用户:http://localhost/users get方法提交;

查询文章:http://localhost/articles?author=1&category=2get方法提交;

错误的例子如下:

http://localhost/get_user

https://api.example.com/getUserInfo?userid=1

https://api.example.com/getusers

https://api.example.com/sv/u

https://api.example.com/cgi-bin/users/get_user.php?userid=1

1.5动词

  1. RESTful
    的核心思想就是,客户端发出的数据操作指令都是"动词 + 宾语"的结构,动词通常就是四种 HTTP 方法,对应 CRUD 操作:

GET(SELECT):从服务器取出资源(一项或多项)。

POST(CREATE):在服务器新建一个资源。

PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。

PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。

DELETE(DELETE):从服务器删除资源。

其中

(1)删除资源 必须 用 DELETE 方法

(2)创建新的资源 必须 使用 POST 方法

(3)更新资源 应该 使用 PUT 方法

(4)获取资源信息 必须 使用 GET 方法

针对每一个路径来说,下面列出所有可行的 HTTP 动词和端点的组合


请求方


URL


描述



GET


/zoos


列出所有的动物园(ID和名称,不要太详细)


POST


/zoos


新增一个新的动物园


GET


/zoos/{zoo}


获取指定动物园详情


PUT


/zoos/{zoo}


更新指定动物园(整个对象)


PATCH


/zoos/{zoo}


更新动物园(部分对象)


DELETE


/zoos/{zoo}


删除指定动物园


GET


/zoos/{zoo}/animals


检索指定动物园下的动物列表(ID和名称,不要太详


细)


GET


/animals


列出所有动物(ID和名称)。


POST


/animals


新增新的动物


GET


/animals/{animal}


获取指定的动物详情


PUT


/animals/{animal}


更新指定的动物(整个对象)


PATCH


/animals/{animal}


更新指定的动物(部分对象)


GET


/animal_types


获取所有动物类型(ID和名称,不要太详细)


GET


/animal_types/{type}


获取指定的动物类型详情


GET


/employees


检索整个雇员列表


GET


/employees/{employee}


检索指定特定的员工


GET


/zoos/{zoo}/employees


检索在这个动物园工作的雇员的名单(身份证和姓名)


POST


/employees


新增指定新员工


POST


/zoos/{zoo}/employees


在特定的动物园雇佣一名员工


DELETE


/zoos/{zoo}/employees/{employee}


从某个动物园解雇一名员工

1.6入参

1、如果记录数量很多,服务器不可能都将它们返回给用户。API 应该 提供参数,过滤返回结果。下面是一些常见的参数。

  • ?limit=10:指定返回记录的数量
  • ?o?set=10:指定返回记录的开始位置。
  • ?page=2&per_page=100:指定第几页,以及每页的记录数。
  • ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  • ?animal_type_id=1:指定筛选条件

所有URL参数 必须是全小写,必须使用下划线类型的参数形式。

分页参数 必须 固定为 page 、 per_page

经常使用的、复杂的查询 应该 标签化,降低维护成本,如

GET /trades?status=closed&sort=sortby=name&order=asc

#   可为其定制快捷方式

GET /trades/recently_closed

2、入参可分为业务参数和公共参数;公共参数有:


参数


名称


说明


timestamp


时间戳


clientid


调用方appid


统一管理应用,否则不放行


token


令牌


幂等情况可用


version


版本号

1.7响应

1、出参(返回值):必须的字段有:


字段


类型


描述


code


数值


状态码


msg


字符串


信息描述


data


结果集


返回结果集

2、如果请求处理完全正确,则状态码为0 ;

3、状态码暂定8位数数字,前4位为某一个应用(服务)拟的一个数字,后4位为具体的状态值。状态码分为2种---公共和自定义,公共码以0打头+3位数。

比如:

99990400  --客户端错误,比如请求语法格式错误、无效的请求、无效的签名等。

99991001  -----用户Id不能为空

响应的公共码如下:


编码


描述


说明


001


注解使用错误


 


002


微服务不在线,或网络超时


 


003


TOKEN解析失败


 


004


TOKEN无效或没有对应的用户


 


400


客户端错误,比如请求语法格式错误、

无效的请求、无效的签名等。


服务器 应该 放弃该请求


401


需要身份认证,比如access_token 无效/过期


客户端在收到 401 响应后,

都 应该 提示用户进行下一步的登录操作


403


没有权限访问该请求


服务器收到请求但拒绝提供服务。

如当普通用户请求操作管理员用户时,

必须 返回该状态码


404


用户请求的资源不存在


如获取不存在的用户信息


410


请求的资源不存在,并且未来也不会存在


在收到 410 状态码后,

客户端 应该 停止再次请求该资源。


429


请求次数超过允许范围


 


500


未知异常


应该 提供完整的错误信息支持,也方便跟踪调试

1.8项目结构

 

1、采用经典DDD领域取到模型:(默认一个解决方案有5个项目)

5个项目分别为:

Web层为最外层接口定义;

Service为具体的应用服务处理;

Infrastructure基础设施层,处理具体的业务逻辑和数据DB的处理;

Domain领域层为模型和仓库接口interface;

Common为通用的一些Helper类;

2、一个解决方案创建5个项目(如上图),并且里包含常用的基础组件:Log4net日志,听云监听;dockerfile,skywalking,全局异常捕捉,接口请求开始和结束的日志记录,swagger,service层的依赖注入,Mapping等。

3、代码全部采用依赖注入写法,尽量少些静态类;

4、HttpClient的写法:使用采用.netcore官方提供的方法,采用工厂类+依赖注入方式:实例代码如下:

1、SartUp类里添加代码-- httpclient初始化:
   services.AddHttpClient("MsgApi", c =>
            {
                c.BaseAddress = new Uri(Configuration["OuterApi:MsgApi:url"]);
                c.Timeout = TimeSpan.FromSeconds(30);
            });

//2 构造函注入
private IDbContext _dbContext;
private IUnitOfWork _unitOfWork;
private IordersRepository _ordersRepository;
private IordercourseRepository _ordercourseRepository;
private ILogger _logger;
privatereadonly IConfiguration _config;
privatereadonly IHttpClientFactory _clientFactory;

public ordersService(IDbContext dbContext, ILogger<ordersService> logger, IConfiguration config, IHttpClientFactory clientFactory)
        {
            _dbContext = dbContext;
            _unitOfWork = new UnitOfWork(_dbContext);
            _ordersRepository = new ordersRepository(_dbContext);
            _ordercourseRepository = new ordercourseRepository(_dbContext);
            _mapper = mapper;
            _config = config;
            _logger = logger;
            _clientFactory = clientFactory;
        }

//3使用
///<summary>
///判断此时该校区是否可以下单
///</summary>
///<param name="req"></param>
///<returns></returns>
publicasync Task<Result<string>> CheckDept(CheckSchoolDeptReq req)
        {
            Result<string> sendRet = new Result<string>();
try
            {
                HttpClient client = _clientFactory.CreateClient("ContractApi");
                MyHttpClientHelper myHttpClientHelper = new MyHttpClientHelper();
                MarketToUPCCheckReq checkreq = new MarketToUPCCheckReq();
                sendRet = await myHttpClientHelper.GetData<Result<string>>(client, "MarketToUPCCheck", checkreq);
            }
catch (Exception ex)
            {
                sendRet.state = false;
                sendRet.error_code = ErrorCode.SysExceptionError;
                sendRet.error_msg = "调用《是否可以下订单接口》报错了。请重试或者联系管理员!";
                _logger.LogError(ex, ErrorCode.SysExceptionError +"调用《是否可以下订单》接口报错了:" + ex.Message);
            }

return sendRet;
        }

1.9日志

 

1、接口开始前和结束后都已在LogstashFilter里记录,接口里就不需要再次记录;

LogstashFilter里的代码如下:

 /// <summary>
    /// 记录日志用过滤器
    /// </summary>
    public class LogstashFilter : IActionFilter, IResultFilter
    {
        private string ActionArguments { get; set; }

        /// <summary>
        /// 请求体中的所有值
        /// </summary>
        private string RequestBody { get; set; }
        private Stopwatch Stopwatch { get; set; }

        private ILogger _logger;

        public LogstashFilter(ILogger<LogstashFilter> logger )
        {
            _logger = logger;

        }

        /// <summary>
        /// Action 调用前执行
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context)
        {

            long contentLen = context.HttpContext.Request.ContentLength == null ? 0 : context.HttpContext.Request.ContentLength.Value;
            if (contentLen > 0)
            {
                // 读取请求体中所有内容
                System.IO.Stream stream = context.HttpContext.Request.Body;
                if (context.HttpContext.Request.Method == "POST")
                {
                    stream.Position = 0;
                }
                byte[] buffer = new byte[contentLen];
                stream.Read(buffer, 0, buffer.Length);

                RequestBody = System.Text.Encoding.UTF8.GetString(buffer);// 转化为字符串
            }

            ActionArguments = JsonConvert.SerializeObject(context.ActionArguments);

            Stopwatch = new Stopwatch();
            Stopwatch.Start();

            string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
            string method = context.HttpContext.Request.Method;

            _logger.LogInformation($"地址:{url} \n " +
               $"方式:{method} \n " +
               $"请求体:{RequestBody} \n " +
               $"完整参数:{ActionArguments}\n " );

        }

        /// <summary>
        /// Action 方法调用后,Result 方法调用前执行
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do nothing
        }

        /// <summary>
        /// Result 方法调用前(View 呈现前)执行
        /// </summary>
        /// <param name="context"></param>
        public void OnResultExecuting(ResultExecutingContext context)
        {
            // do nothing
        }

        /// <summary>
        /// Result 方法调用后执行
        /// </summary>
        /// <param name="context"></param>
        public void OnResultExecuted(ResultExecutedContext context)
        {

            Stopwatch.Stop();
            string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
                string method = context.HttpContext.Request.Method;
                string qs = ActionArguments;
                string res = "在返回结果前发生了异常";
                if (context.Result is ObjectResult)
                {
                    dynamic result = context.Result.GetType().Name == "EmptyResult" ? new { Value = "无返回结果" } : context.Result as dynamic;
                    if (result != null)
                    {
                        res = JsonConvert.SerializeObject(result.Value);
                    }

                }

                _logger.LogInformation($"地址:{url} \n " +
                    $"方式:{method} \n " +
                    $"请求体:{RequestBody} \n " +
                    $"参数:{qs}\n " +
                    $"结果:{res}\n " +
                    $"耗时:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒");

        }
    }

2、try Catch日志必须要添加LogError日志,并且要将堆栈信息记录,代码如下:

catch (Exception ex)
            {
                _logger.LogError(ex, ErrorCode500 + ex.Message);
            }

原文地址:https://www.cnblogs.com/lonelyxmas/p/12242572.html

时间: 2024-08-29 16:08:22

.netcore 3.1高性能微服务架构:webapi规范的相关文章

.netcore 3.1高性能微服务架构:封装调用外部服务的接口方法--HttpClient客户端思路分析

众所周知,微服务架构是由一众微服务组成,项目中调用其他微服务接口更是常见的操作.为了便于调用外部接口,我们的常用思路一般都是封装一个外部接口的客户端,使用时候直接调用相应的方法.webservice或WCF的做法就是引用服务,自动生成客户端.在webapi2.0里,我们都会手动封装一个静态类.那么在.netcore3.1的微服务时代,我们该如何处理这个问题呢? ----思路都是一样的,封装一个外部服务,并且使用依赖注入和 HttpFactory工厂等.netcore特有的方式提升性能.接下来我们

.netcore 3.1高性能微服务架构:为什么要采用.netcore3.1作为微服务技术栈

自动.netcore升级到3.0以后,.net圈子更活跃了,写博客的人也越来越多,很多人开通了公众号写文章:也有技术大牛神乘这波“红利”写书录视频教程等赚钱了.大公司也开始关注.net技术,采用.net作为技术栈的公司大部分都将项目升级到.netcore,以前从.net转Java的人也有部分回来了.总之,.netcore生态空前的变好了.所有.net的程序员们,建议直接学习.netcore新技术,不要在呆在.netframework技术里泥潭了. 作为.net开发的老人,我已经有10年的开发和项

什么是微服务架构,.netCore微服务选型

什么是微服务架构,.netCore微服务选型 https://www.cnblogs.com/uglyman/p/9182485.html 开发工具:VS2017 .Net Core 2.1 什么是微服务? 单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题,整个系统就瘫痪了:一旦升级其中一个模块,整个系统就停机了: 3)要上线必须一起上线,互相等待,无法快速响应需求: 4)集群只能是复制整个系统,即使只是其中一个

(1).NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点:1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块:2)系统耦合性强,一旦其中一个模块有问题,整个系统就瘫痪了:一旦升级其中一个模块,整个系统就停机了:3)要上线必须一起上线,互相等待,无法快速响应需求:4)集群只能是复制整个系统,即使只是其中一个模块压力大: 微服务:不同模块放到不同的进程/服务器上,模块之间通过网络通讯进行协作.适用于:模块比较多,访问量比较大的互联网类系统,并不是所有项目都

腾讯开源微服务架构 Tars,高性能 RPC 开发框架

腾讯微服务架构 Tars 于今日正式开源. Tars 取名于电影“星际穿越”中的机器人,是支持多语言的高性能 RPC 开发框架和配套一体化的服务治理平台,可以帮助企业或者用户以微服务的方式快速构建稳定可靠的分布式应用.据称,Tars 是腾讯内部使用将近十年的基于微服务的统一应用框架 TAF(Total Application Framework),目前有在100多个业务(如手机浏览器.应用宝.手机管家.手机QQ.手机游戏等).1.6多万台服务器上运行使用. 平台特性 tars 协议:tars协议

Java高并发高性能分布式框架从无到有微服务架构设计

微服务架构模式(Microservice Architect Pattern).近两年在服务的疯狂增长与云计算技术的进步,让微服务架构受到重点关注 微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调.互相配合,为用户提供最终价值.每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API).每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境.类生产环境等.另外,应尽量避免统一的.集中式的服务管理

.NET微服务架构及API网关

一.MSA简介 1.1.MSA是什么 微服务架构MSA是Microservice Architecture的简称,它是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相通讯.互相配合,为用户提供最终价值.它与SOA之间的区别如下: SOA实现 微服务架构实现   企业级,自顶向下开展实施 团队级,自底向上开展实施   粒度大:服务由多个子系统组成 粒度细:一个系统被拆分成多个服务,且服务的定义更加清晰   重ESB:企业服务总线,集中式的服务架构 轻网关:无集中式总线,松散的服务

.Net微服务架构之运行日志分析系统

一.引言 .Net技术栈目前还没有像spring cloud相对完整一整微服务架构栈,随着业务发展系统架构演进,自行构建.Net技术体系的微服务架构,配套相关核心组件.因平台基于微服务架构方式研发,每个领域服务遵循平台统一标准,各自研发,独立部署运行,服务运行日志均通过记录本地文件方式进行记录.程序日志无法及时查阅,需登录服务器查看,同时不利于日志统一管理,因研发运行日志分析系统,进行日志统一分析管理,便于快速定位程序运行问题及时处理,保障平台运行稳定.虽然行业上也有一些日志架构,如较为有名的L

【.net core】电商平台升级之微服务架构应用实战(core-grpc)

一.前言 这篇文章本来是继续分享IdentityServer4 的相关文章,由于之前有博友问我关于微服务相关的问题,我就先跳过IdentityServer4的分享,进行微服务相关的技术学习和分享.微服务在我的分享目录里面是放到四月份开始系列文章分享的,这里就先穿越下,提前安排微服务应用的开篇文章 电商系统升级之微服务架构的应用. 本博客以及公众号坚持以架构的思维来分享技术,不仅仅是单纯的分享怎么使用的Demo. 二.场景 先来回顾下我上篇文章 Asp.Net Core 中IdentityServ