首页 文章 精选 留言 我的

精选列表

搜索[高性能全文搜索引擎],共10000篇文章
优秀的个人博客,低调大师

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

1.1 定义1、基础接口:单一职责原则,每个接口只负责各自的业务,下接db,通用性强。 2、聚合接口:根据调用方需求聚合基础接口数据,业务性强。 1.2 协议 客户端在通过 API 与后端服务通信的过程中, 应该使用 HTTPS(生产环境) 协议 服务端响应的数据格式统一为JSON 1.3域名hostprd环境: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路径pathpath命名应该是以资源为导向的命名,对资源的操作是由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动词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:指定返回记录的数量?offset=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 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使用 //////判断此时该校区是否可以下单/////////publicasync Task> 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> 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); }

优秀的个人博客,低调大师

uCharts 1.9.3 发布,高性能跨平台图表库

支持图表类型 饼图pie 圆环图ring 线图line(直线、曲线) 柱状图column(分组、堆叠、温度计) 区域图area(直线、曲线) 雷达图radar 圆弧进度图arcbar 仪表盘gauge K线图candle 混合图mix(支持 Point、Line 直线曲线、Column、Area 直线曲线) 玫瑰图rose(面积模式、半径模式) 词云图word(横向、横纵混排) 漏斗图funnel 地图map 插件特点 改造后的插件可以跨端使用,支持 H5、小程序(微信/支付宝/百度/头条/QQ/360)、APP,调用简单方便、性能及体验极佳。 虽然没有 Echarts 及 F2 图表功能强大,但可以实现一套业务逻辑各端通用,并解决了支付宝小程序图表显示模糊等问题。 支持单页面多图表,demo 中单页 10 个图表,响应速度超快。 支持入场动画及ToolTip动画效果。 独特支持横屏模式。 为何不用 Echarts? 相比 Echarts 及 F2 的复杂的设置,本插件几乎等于傻瓜式的配置。 Echarts 在跨端使用更复杂,本插件只需要简单的两个<canvas>标签轻松区别搞定,代码整洁易维护。 Echarts 在iOS端图表显示错位,只能引用网页解决。 本插件打包后的体积相比 Echarts 小很多很多,所以性能更好。 如果您是uni-app初学者,那么强烈建议您使用 uCharts,并且目前可以跨全端通用,减少工作量,增强一致性体验。 图表样式均可自定义,懂JS的都可以读懂插件源码,直接修改u-charts.js源码即可。 本插件经过大量测试,反复论证并加以改造而成,请各位放心使用。 uni-app 图表选型参考流程 亲手教您如何改造uCharts,打造您的专属图表 为何要改造 uCharts? 并不是所有图表插件直接拿来就可以满足客户需求,如果您的 UI 设计师设计一个图表,如下图: 您会发现这个图表即使在 echarts 里也不是很好实现,那么就需要我们自己动手去实现。下面就让我们一起来完成,本文旨在抛砖引玉,希望各位朋友能够更好的应用 uCharts 来完成您的项目,如果您有更好的设计,请提交您的 PR 到 GithubuCharts跨端图表,帮助更多朋友,感谢您的付出及贡献! 【uCharts 跨端图表改造教程】 图表示例 快速体验 一套代码编到 7 个平台,依次扫描二维码,亲自体验 uCharts 图表跨平台效果!iOS 因 demo 比较简单无法上架,请自行编译。

优秀的个人博客,低调大师

uCharts 1.9.2 发布,高性能跨平台图表库

支持图表类型 饼图pie 圆环图ring 线图line(直线、曲线) 柱状图column(分组、堆叠、温度计) 区域图area(直线、曲线) 雷达图radar 圆弧进度图arcbar 仪表盘gauge K线图candle 混合图mix(支持 Point、Line 直线曲线、Column、Area 直线曲线) 玫瑰图rose(面积模式、半径模式) 词云图word(横向、横纵混排) 漏斗图funnel 地图map 插件特点 改造后的插件可以跨端使用,支持 H5、小程序(微信/支付宝/百度/头条/QQ/360)、APP,调用简单方便、性能及体验极佳。 虽然没有 Echarts 及 F2 图表功能强大,但可以实现一套业务逻辑各端通用,并解决了支付宝小程序图表显示模糊等问题。 支持单页面多图表,demo 中单页 10 个图表,响应速度超快。 支持入场动画及ToolTip动画效果。 独特支持横屏模式。 为何不用 Echarts? 相比 Echarts 及 F2 的复杂的设置,本插件几乎等于傻瓜式的配置。 Echarts 在跨端使用更复杂,本插件只需要简单的两个<canvas>标签轻松区别搞定,代码整洁易维护。 Echarts 在iOS端图表显示错位,只能引用网页解决。 本插件打包后的体积相比 Echarts 小很多很多,所以性能更好。 如果您是uni-app初学者,那么强烈建议您使用 uCharts,并且目前可以跨全端通用,减少工作量,增强一致性体验。 图表样式均可自定义,懂JS的都可以读懂插件源码,直接修改u-charts.js源码即可。 本插件经过大量测试,反复论证并加以改造而成,请各位放心使用。 uni-app 图表选型参考流程 亲手教您如何改造uCharts,打造您的专属图表 为何要改造 uCharts? 并不是所有图表插件直接拿来就可以满足客户需求,如果您的 UI 设计师设计一个图表,如下图: 您会发现这个图表即使在 echarts 里也不是很好实现,那么就需要我们自己动手去实现。下面就让我们一起来完成,本文旨在抛砖引玉,希望各位朋友能够更好的应用 uCharts 来完成您的项目,如果您有更好的设计,请提交您的 PR 到 GithubuCharts跨端图表,帮助更多朋友,感谢您的付出及贡献! 【uCharts 跨端图表改造教程】 图表示例 快速体验 一套代码编到 7 个平台,依次扫描二维码,亲自体验 uCharts 图表跨平台效果!iOS 因 demo 比较简单无法上架,请自行编译。

优秀的个人博客,低调大师

uCharts 1.9.0 发布,高性能跨平台图表库

新增地图图表类型map;新增多Y轴绘制,支持全部直角坐标系类图表类型。 更新记录 未来计划(V1.0 至 2.0 版本) 2019.08.xx 推迟计划加入第二种仪表盘样式,增加《亲手教您如何改造 uCharts,打造您的专属图表》教程。 2019.08.xx 计划加入渐变颜色填充功能,支持柱状图、区域图、混合图、圆弧进度条。 2019.08.xx 计划修复折线图样式为曲线时,个别情况下曲线超出X轴的问题。 未来计划(全新 V2.0 版本) 2019.5.20 重要又浪漫的日期,uCharts 团队正式成立了。 我们将以追求极致、追求完美的极客精神来打造 uCharts。uCharts2.0 版本正在前期策划中,将以全新结构重写 uCharts,支持多Y轴、多X轴、自定义图例位置、以及更多图表类型,请各位敬请期待。 支持图表类型 饼图pie 圆环图ring 线图line(直线、曲线) 柱状图column(分组、堆叠、温度计) 区域图area(直线、曲线) 雷达图radar 圆弧进度图arcbar 仪表盘gauge K线图candle 混合图mix(支持 Point、Line 直线曲线、Column、Area 直线曲线) 玫瑰图rose(面积模式、半径模式) 词云图word(横向、横纵混排) 漏斗图funnel 地图map 插件特点 改造后的插件可以跨端使用,支持 H5、小程序(微信/支付宝/百度/头条/QQ/360)、APP,调用简单方便、性能及体验极佳。 虽然没有 Echarts 及 F2 图表功能强大,但可以实现一套业务逻辑各端通用,并解决了支付宝小程序图表显示模糊等问题。 支持单页面多图表,demo 中单页 10 个图表,响应速度超快。 支持入场动画及ToolTip动画效果。 独特支持横屏模式。 为何不用 Echarts? 相比 Echarts 及 F2 的复杂的设置,本插件几乎等于傻瓜式的配置。 Echarts 在跨端使用更复杂,本插件只需要简单的两个<canvas>标签轻松区别搞定,代码整洁易维护。 Echarts 在iOS端图表显示错位,只能引用网页解决。 本插件打包后的体积相比 Echarts 小很多很多,所以性能更好。 如果您是uni-app初学者,那么强烈建议您使用 uCharts,并且目前可以跨全端通用,减少工作量,增强一致性体验。 图表样式均可自定义,懂JS的都可以读懂插件源码,直接修改u-charts.js源码即可。 本插件经过大量测试,反复论证并加以改造而成,请各位放心使用。 uni-app 图表选型参考流程 亲手教您如何改造uCharts,打造您的专属图表 为何要改造 uCharts? 并不是所有图表插件直接拿来就可以满足客户需求,如果您的 UI 设计师设计一个图表,如下图: 您会发现这个图表即使在 echarts 里也不是很好实现,那么就需要我们自己动手去实现。下面就让我们一起来完成,本文旨在抛砖引玉,希望各位朋友能够更好的应用 uCharts 来完成您的项目,如果您有更好的设计,请提交您的 PR 到 GithubuCharts跨端图表,帮助更多朋友,感谢您的付出及贡献! uCharts 跨端图表改造教程(暂未完成,请关注) 图表示例 快速体验 一套代码编到 7 个平台,依次扫描二维码,亲自体验 uCharts 图表跨平台效果!iOS 因 demo 比较简单无法上架,请自行编译。

优秀的个人博客,低调大师

《高性能Linux服务器构建实战》笔记

系统安全、故障排查、自动化运维、集群架构 1、账号安全,安装完后可以删除的账号: adm,lp,sync,shutdown,halt,news,uucp,operator,games,gopher 禁用登录功能:usermod -s /sbin/nologin nagios 2、关闭不需要的服务: anacron auditd autofs avahi-daemon avahi-dnsconfd bluetooth cpuspeed firstboot gpm haldaemon hidd ip6tables ipsec isdn lpd mcstrans messagebus netfs nfs nfslock nscd pcscd portmap readahead_early restorecond rpcgssd rpcidmapd rstatd sendmail setroubleshoot yppasswdd ypserv 系统必须运行的服务:

优秀的个人博客,低调大师

架构高性能网站秘笈(二)——动态内容缓存

什么是动态内容缓存? 浏览器向服务器发送请求后,服务器会根据浏览器的要求做相应的处理(如:数据库操作),然后将处理后的结果注入JSP页面生成HTML,最后将生成的HTML返回给浏览器显示。我们知道,数据库读取操作是非常耗时的,如果能将每次请求中的数据库处理时间去掉,那服务器的相应速度将会大幅提升。要实现这一点,我们就需要将常用的HTML页面事先生成好,当用户发出请求时,服务器只需从缓存中取出即可,无需再做数据库处理操作。 综上所述:事先生成HTML页面的技术称为动态内容缓存。 什么是“缓存命中率”? 缓存命中率 = 访问缓存的请求数/请求总数 缓存命中率是衡量缓存是否有效的重要指标。如果将所有需要访问的数据均缓存起来,那么缓存命中率是100%。但一半情况下,为了节约存储空间,只将访问频率较高的数据缓存起来,那么这样就会造成有一些请求能够通过缓存访问数据,而有一些请求需要查询数据库访问数据。 我们没有办法确保缓存的命中率100%,但我们需要使用一些算法确保缓冲命中率较高。也只有当缓冲命中率较高的情况下,缓冲才能发挥它真正的价值。 PS:缓存与缓冲的区别? 这里顺便提一下“缓存”和“缓冲”的区别。 缓冲:缓冲是一块临时的存储空间,为了解决不同存储设备之间读写速率不匹配而产生的。比如:内存的读写速度远远大于硬盘的读写速度。当内存中的数据向硬盘中写入时,内存会先将数据写入内存缓冲区,再由内存缓冲区向硬盘写入数据。这样内存快速腾空之后就可以做其他事了,提高了效率。 缓存:缓存是在一些为了避免重复计算,而被暂时性存储的数据。 综上所述:缓冲=缓冲区,它是指内存中的一片区域;而缓存指的是数据,一些味了避免重复计算而被临时性存储的数据。 它们都是为了提高处理效率而存在的,都采用了“牺牲空间换取时间”的思想。 缓存的三种存储方式 缓存可以存储在三种不同的地方: 存储在内存 存储在IO设备 存储在内存和IO设备上 存储在缓存服务器上 对于小型网站,缓存首选的存储位置就是内存。内存相对于IO设备有较快的访问速度,能大大缩短缓存的读取时间。但内存资源比较宝贵,如果没钱买大内存的服务器的话,那就只好考虑第二种办法,将缓存存在IO设备中。对于一些小型网站,如果需要存储的缓存数据量比较大,而且买不起大内存的服务器的话,那存储在IO设备是不错的选择。但IO设备相对于内存来说有个致命的缺陷,那就是慢!因此,对于资金不充裕的小型网站来说,第三种方式最为合适。如果既想获得内存的读写速度,又想拥有IO设备的巨大存储空间,那就可以选择两者结合的方式。内存中开辟一块固定大小的缓冲区,用于存储使用频率较高的缓存数据,而将使用频率较低的缓存数据存储至IO设备。当缓冲区存满时,你需要使用LRU算法将最近最旧未使用的缓存从缓冲区中扔出去,而将最近一段时间IO设备中访问频率最高的缓存放进来,从而保证了当前缓冲区中数据的命中可能性是最大的。最后,对于大型网站来说,可以使用专门的缓存服务器来存储缓存。这主要有两点好处: 节约服务器的内存资源,让服务器的内存作更多数据处理的工作,而把数据存储的工作交给缓存服务器; 缓存服务器具有更好的可扩展性。如果网站需要定期举办一些营销活动,为了应付急剧增加的并发量,缓存服务器是不二之选。 但是,由于Web服务器与缓存服务器采用TCP通信,而建立和释放TCP连接时间开销比较大,因此对于小型网站来说,直接将缓存存在内存无疑是种首选办法。 如何实现缓存? 这里就简单地介绍下基于内存的缓存机制,近期我会单独写一篇“基于内存+IO+LRU算法的缓存机制”的博客,敬请关注。 服务器在接收客户端发来的请求后,首先根据请求的URL判断内存中是否有对应的缓存;若有的话先判断该缓存是否过期,若没过期就直接返回缓存数据;若过期了,或者内存中根本就没有该请求的缓存的话,就调用业务逻辑层的相关函数,处理客户端发来的请求,生成客户端需要的数据。然后给生成的数据设置一个有效期,并存入缓存,供下次使用,最后将数据返回给用户。 静态化页面 之前我们介绍的缓存机制仍然需要服务器程序去判断存储空间中是否有缓存,而服务器程序判断的过程也需要时间,如果缓存很多的话,服务器程序需要找半天。那么我们能否把静态页面事先都生成好,让浏览器直接访问静态页面,而无需再通过服务器程序访问缓存。 要知道,浏览器直接访问静态页面的速度比通过服务器程序访问静态页面的速度要快很多很多很多! 要实现这样的想法,我们可以这么做: 服务器程序启动的时候将生成所有需要访问的HTML页面,存储至服务器外设中; 浏览器所有a标签中的href都填写这些静态HTML页面URL; 在服务器中开启一条定时线程,每隔一段时间检查动态数据发生修改的HTML页面,并生成新的HTML页面; 当数据库发生修改时,立即更新与该数据相关的HTML页面。

优秀的个人博客,低调大师

Facebook构建高性能Android视频组件实践之路

作者|Udi Cohen译者|孙浩 本文转自“移动开发前线”(微信号:bornmobile)。随着移动设备视频消费的快速增长,为了更高效地呈现内容,Facebook 的移动工程师们面临着新的挑战。比起文本和图片这样的简单 UI 元素,视频需要更多的资源。视频解码器会消耗很多 CPU 资源,创建视频时还需要分配大量内存,同时他们会使用更多的网络带宽来从服务器下载视频数据。 在可滚动的容器中播放视频其实是有一定难度的——对设备资源的压力可能会导致视频丢帧,这会使滚动效果变的很差。此外,我们不希望用户等待视频加载,我们也不希望它在播放中碰到缓冲,因此视频播放器需要快速启动并流畅运行。纵观市场上的各种设备,这些问题在 Android 上的挑战还是很大的。 我们最近完成了 Android 平台的新闻提要技术迁移,由我们的开源 UI 渲染框架 Litho 提供技术支持。Litho 支持异步布局和细粒度的回收利用,这不仅有助于优化新闻提要使其更有效地渲染内容,而且可以使我们的代码更健壮、更易于扩展。我们希望将这些改进带到视频功能上来,以改善 Facebook 用户的播放体验,同时也为设计新用例的工程师们提供帮助。 构建一个视频 UI 元素比我们所做的其他 UI 组件更具挑战性。视频组件使用了 Litho 的一些其他组件不需要的高级特性,同时也促使 Litho 创建了一些新的特性。新的 Litho 视频组件引入了一些改进,从新闻提要的滚动性能到更灵活的设计,这些设计可以很容易地在不同的视频功能中重用。这篇文章描述了我们在 Android 应用程序中重写视频播放器时所面临的挑战,以及 Litho 如何帮助克服这些困难的。 视频新闻 新闻提要支持多种视频新闻类型。有些类型的行为和外观与其他的有所不同。视频附件是一种常见的新闻类型,其中有常规视频、Facebook Live 视频、360 度视频、gif 或其他类型的视频,它们会附在一个普通的文章上。 其他的视频新闻类型可以播放生成的视频,赞助商的信息,或者短动画。 想要支持所有这些变种会导致代码难以维护和测试。我们提供视频附件使用的主视频视图类叫 VideoAttachmentView。对于一些新闻类型,我们必须扩展这个视图,以便重用和定制它来适应设计的需要。在某些情况下,我们不能在派生类中进行所有的更改,只能创建一个单独的视图类,这意味着需要将通用逻辑移到帮助类中。这进一步使代码复杂化了。 新闻提要的滚动性能也会受到影响。RecyclerView 仅从相同类的类型中回收视图,这意味着不能为常规视频附件新闻类型回收像 SponsoredVideoAttachmentView 这样的视图,尽管 SponsoredVideoAttachmentView 继承至 VideoAttachmentView。由于这种低效率的原因,RecyclerView 需要分配更多的视图来容纳不同的新闻类型,从而导致了更多的内存占用。另外,在视频视图展现在屏幕上之前,分配行为就要被触发,这增加了它无法及时完成的可能性,甚至可能会导致丢帧。即使在一个新的布局中重用相同的 VideoAttachmentView 来优化它的表现,我们也需要创建一个新的视图类型。因为我们另外添加了一层,这也使得视图层次结构变的更深了。视图层次越深,渲染的时间就越长。 设计视频组件 Litho 用一种叫做“组件”的单位来定义 UI。它支持两种主要类型的组件:MountSpec 和 LayoutSpec。MountSpec 定义了一个 UI 构建块,例如一个文本或图像组件。LayoutSpec 定义一个包含一个或多个组件的布局。在 Litho 中,使用组件结构来构造更复杂的组件是很常见的做法。LayoutSpec 可以定义一个包含其他 LayoutSpec 和 MountSpec 组件的布局,MountSpec 可以呈现一个特定的视图或绘制视图。 视频组件是一个 UI 构建块,这意味着我们需要一个 MountSpec 来定义它。一种方法是创建一个 MountSpec 来呈现 VideoAttachmentView,但我们还需要为其它扩展视图创建另一个 MountSpec, 如 SponsoredVideoAttachmentView。虽然这做也是可行的,但最终我们还会遇到相同的重用性和可维护性的问题。另一种方法是创建一个 MountSpec 只呈现简单的视频视图而不是 VideoAttachmentView,并将其添加到一个特定的新闻类型的 LayoutSpec 中。下图演示了新组件之间的关系: CoreVideoComponent 是一个有着最简特性的任何视频新闻都需要的 MountSpec。 @MountSpec public class CoreVideoComponentSpec { @OnCreateMountContent static SimpleVideoView onCreateMountContent(ComponentContext context) { return new SimpleVideoView(context); } @OnPrepare static void onPrepare( ComponentContext context, @Prop VideoParams videoParams) { prefetchVideo(videoParams); } @OnMount static void onMount( ComponentContext context, SimpleVideoView videoView, @Prop VideoParams videoParams) { initVideoPlayback(videoView, videoParams); } @OnUnmount static void onUnmount( ComponentContext context, SimpleVideoView videoView, @Prop VideoParams videoParams) { cleanupVideoPlayback(videoView, videoParams); } ... } CoreVideoComponent 是 AutoplayVideoComponent 的子类,该组件是一个用于在新闻提要中注册视频的 LayoutSpec。所有新闻提要中的视频都是在自动播放管理器上注册的,但并不是所有的视频都需要自动播放功能 (例如,全屏视频播放器中的视频)。 @LayoutSpec public class AutoplayVideoComponentSpec { @OnCreateLayout static Component onCreateLayout( ComponentContext c, @Prop VideoParams videoParams) { registerVideoForAutoplay(videoParams); return CoreVideoComponent.create(c) .videoParams(videoParams) .build(); } ... } 最后, 我们将自动播放组件作为子类添加到 VideoAttachmentComponent 中。这个组件将一个视频附件数据结构转换为一个通用的视频组件都能理解的属性。 public class VideoAttachmentComponentSpec { @OnCreateLayout static Component onCreateLayout( ComponentContext c, @Prop VideoAttachmentInfo videoAttachmentInfo) { final VideoParams videoParams = createVideoParams(videoAttachmentInfo); return AutoplayVideoComponent.create(c) .videoParams(videoParams) .build(); } ... } 与 VideoAttachmentView 相比,这个设计提供了更多的灵活性。这些组件中的任何一个都可以添加到另一个 LayoutSpec 中,创建一个更复杂的组件并扩展它的功能或 UI 设计。Litho 鼓励使用嵌套组件,以及组件组合,以构建更强大的功能。Litho 以最优的渲染性能优化了布局树,构建出了扁平的视图结构。 下面是一个创建视频附件组件的示例,该组件显示底部的水印: @LayoutSpec public class WatermarkVideoAttachmentComponentSpec { @OnCreateLayout static Component onCreateLayout( ComponentContext c, @Prop VideoAttachmentInfo videoAttachmentInfo) { return Column.create(c) .flexShrink(0) .alignContent(YogaAlign.FLEX_START) .child( VideoAttachmentComponent.create(c) .videoAttachmentInfo(videoAttachmentInfo)) .child( Text.create(c) .text("Powered by Litho") .textColorRes(android.R.color.holo_green_light) .textSizeDip(25) .paddingDip(YogaEdge.LEFT, 5) .positionType(YogaPositionType.ABSOLUTE) .positionDip(YogaEdge.BOTTOM, 0) .positionDip(YogaEdge.START, 0)) .build(); } } 新组件通过将其添加为子组件来重新使用视频附件组件的所有代码和 UI。 性能改进 除了支持更加灵活的设计之外,Litho 还提供了一些属性和特性,帮助我们优化新闻提要中的视频播放和整个应用的整体性能。 资源回收利用 Android 内置的 RecyclerView 可以基于视图的类型将其保存在不同的缓存池中,这对于创建了很多不同类型视图的用户界面来说可能会是一个问题。 相比之下,Litho 的回收系统复用了更小的用户界面构建模块,比如文本或图片,而不是整个视图。通过使用一个核心视频组件,同样的视图可以被循环使用于所有的视频新闻类型。更有效的回收利用减少了对象的分配,进而提高了滚动性能。 预分配 新闻提要的第一个视频新闻不能循环使用预先存在的视频视图,因为之前没有视图。当两个视频新闻同时出现在屏幕上时也需要注意:一个视频视图可以从以前的新闻中回收,但是第二个视图需要新建。当 RecyclerView 需要分配一个新的视图对象,特别是像视频视图那样的复杂视图时,会带来丢帧的风险。我们希望优化这种情况,因此我们在 Litho 中创建了预分配功能。 通过向 MountSpec 注解中添加一些属性,我们可以让 Litho 提前创建一些实例。当滚动浏览新闻提要中的第一个视频新闻时,预分配的视频视图可以极大地提高滚动性能。 @MountSpec(poolSize = 3, canPreallocate = true) public class CoreVideoComponentSpec { ... } 生命周期 MountSpec 有一些实用且简单的生命周期回调方法。这些足以让我们将大部分视频播放逻辑封装在组件中。在 Litho 之前,这个逻辑会被分散到不同的类中,由一个单独的控制器触发。视频组件中的主要回调方法包括: onPrepare- 开始预取视频。在视频组件出现之前,在后台线程上触发。 onMount- 初始化视频播放器。组件首次配置其视图属性时触发。 onUnmount- 清除视频播放器,为下一次使用做准备。当视频滚动走时被触发。 LayoutSpec 有一个主要的回调:onCreateLayout()。它的主要目的是构造 LayoutSpec 的布局,但是它也可以为其子组件准备资源。例如,封面照片 LayoutSpec 可以在上面创建一个带有视频和封面照片的布局,同时还可以触发封面照片的预抓取,所有这些都是在同一个回调方法中进行的。 MountSpec 还支持另一个实用的回调:shouldUpdate()。当 RecyclerView 的适配器被更新时,它可以重新绑定所有的子视图,并获得所有可见的组件并重新加载 (触发 onUnmount 和 onMount)。对于简单的组件,这没有明显的影响,但是重新配置一个视频播放器就会是一个比较繁重的操作。这个回调是在 Litho 重新加载组件之前调用的,如果你觉得它没有必要的话 (例如,加载相同的视频),我们可以选择跳过它。 @ShouldUpdate(onMount = true) static boolean shouldUpdate(@Prop Diff<VideoParams> videoParamsDiff) { return videoParamsDiff.getNext().videoId != videoParamsDiff.getPrevious().videoId; } 可测试性 新组件的模块化有助于更轻松地测试视频播放逻辑。Litho 提供了一个测试框架,可以在单元测试中模拟组件的生命周期。通过将更多的视频播放逻辑封装在这些组件中,我们可以测试和验证我们以前无法使用的复杂场景。此外,通过使用组合而不是继承来扩展功能,会更安全、更容易维护。 结 论 通过使用传统的 Android 视图,Litho 帮助我们通过一次构建就可以提升视频实现的性能、效率、可扩展性和可维护性。该视频组件在整个 Facebook 的 Android 应用程序中提升了 20% 的滚动性能,同时也改善了我们的冷启动时间,使我们的内存崩溃率降低了 2.5%,这都是更有效的内存管理的结果。代码的改进不仅改善了 Facebook 的体验,还让工程师有了更多在 Facebook 应用中创建新的视频体验的余地。 Litho 允许我们在 Android 上编写高度优化的 UI。Litho 于 2017 年 4 月开源,此后一直在成长。在去年年底,我们启动了 Sections 项目,它是基于 Litho 构建的用于编写高度优化的列表界面的 API。转换到 Litho 和 Sections 后,通常会有大幅度的性能提升,比如滚动性能提升可能高达 42%。使用 Litho 很简单并且文档丰富。可以查看 Litho 网站和 GitHub 页面上的代码示例、教程和高级指南。 相关链接 Litho 网站: https://fblitho.com/docs/getting-started GitHub 代码: https://github.com/facebook/litho 英文原文: https://code.facebook.com/posts/2032921950067737/improving-android-video-on-news-feed-with-litho

优秀的个人博客,低调大师

高性能缓存服务器Varnish架构配置

Varnish跟Squid都是一款内容加速缓存服务器,我们可以使用它们来对我们的网页内容进行缓存,以此来从某个方面提高用户体验度,提升网站整体的抗压能力。 目前自建的CDN中,有很多都是基于Squid、Varnish等相关缓存软件,经过内部的二次开发实现了更好的内容加速及管理。 那今天我们一起来学习一下Varnish简单的搭建及日常的维护,深入的东西后期分享,跟大家一起来交流。这里直接上Shell脚本自动初始化并安装: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 #!/bin/sh #autoinstallvarnish #2014-07-28bywugk DIR= /usr/src CODE=$? VER=$1 if [-z$1]; then echo "Usage:{$0'Version'install|config|start|help},Example$02.1.5install}" exit fi URL=https: //repo .varnish-cache.org /source/varnish- ${VER}. tar .gz function install () { if [!-d /usr/local/varnish -o!-f /etc/init .d /varnishd ]; then cd $DIR;wget-c$URL if [$CODE== '0' ]; then echo "ThisvarnishFilesisDownloadSuccessed!" else echo "ThisvarnishFilesisDownloadFailed!" fi useradd -s /sbin/noin varnish mkdir -p /data/varnish/ {cache,} chown -Rvarnish.varnish /data/varnish/ tar xzfvarnish-${VER}. tar .gz; cd varnish-${VER}; /bin/sh autogen.sh;. /configure --prefix= /usr/local/varnish -- enable -dependency-tracking-- enable -debugging-symbols-- enable -developer-warnings- enable -extra-warnings&& make && make install else echo "ThisVarnishisexists,Pleaseexit..." sleep 1 exit 0 fi } function config() { if [!-d /usr/local/varnish/ -o-f /etc/init .d /varnishd ]; then echo "Youcan'tconfigvarnish,Pleaseensureyouvarnishdirisornotexist..." exit 0 else echo "VarnishAlreadySuccessInstall,PleaseConfigvarnish......" fi mv /usr/local/varnish/etc/varnish/default .vcl /usr/local/varnish/etc/varnish/default .vcl.bak cat >> /usr/local/varnish/etc/varnish/default .vcl<<EOF backendserver_1 { .host= "192.168.149.128" ; .port= "8080" ; .probe={ .timeout=5s; .interval=2s; .window=8; .threshold=5; } } backendserver_2 { .host= "192.168.149.129" ; .port= "8080" ; .probe={ .timeout=5s; .interval=2s; .window=8; .threshold=5; } } directorrsverrandom{ { .backend=server_1; .weight=6; } { .backend=server_2; .weight=6; } } aclpurge{ "localhost" ; "127.0.0.1" ; } subvcl_recv { if (req.http.host~ "^(.*).tdt.com" ) { set req.backend=rsver; } else { error200 "Nocahceforthisdomain" ; } if (req.request== "PURGE" ) { if (!client.ip~purge) { error405 "Notallowed." ; } else { return (pipe); } } if (req.http.x-forwarded- for ) { set req.http.X-Forwarded-For= req.http.X-Forwarded-For "," client.ip; } else { set req.http.X-Forwarded-For=client.ip; } if (req.request!= "GET" &&req.request!= "HEAD" ) { return (pipe); } if (req.http.Expect) { return (pipe); } if (req.http.Authenticate||req.http.Cookie) { return (pass); } if (req.http.Cache-Control~ "no-cache" ) { return (pass); } if (req.url~ "\.jsp" ||req.url~ "\.php" ) { return (pass); } else { return (lookup); } }subvcl_pipe { return (pipe); }subvcl_pass { return (pass); }subvcl_hash { set req. hash +=req.url; if (req.http.host) { set req. hash +=req.http.host; } else { set req. hash +=server.ip; } return ( hash ); }subvcl_hit { if (req.request== "PURGE" ) { set obj.ttl=0s; error200 "Purged." ; } if (!obj.cacheable) { return (pass); } return (deliver); }subvcl_miss { if (req.request== "PURGE" ) { error404 "Notincache." ; } if (req.http.user-agent~ "spider" ) { error503 "Notpresentlyincache" ; } return (fetch); } subvcl_fetch { if (req.request== "GET" &&req.url~ "\.(txt|js)$" ) { set beresp.ttl=3600s; } else { set beresp.ttl=30d; } if (!beresp.cacheable) { return (pass); } if (beresp.http.Set-Cookie) { return (pass); } return (deliver); } subvcl_deliver{ if (obj.hits>0){ set resp.http.X-Cache= "HITFROMTDTWSCacheCenter" ; } else { set resp.http.X-Cache= "MISSFROMTDTWSCacheCenter" ; } return (deliver); } EOF if [$?==0]; then echo '----------------------------------' sleep 2 echo "ThisVarinshConfigSuccess!!!" else echo "ThisVarinshConfigFailed,PleaseCheckConf!" fi } function start() { if [!-d /usr/local/varnish -o-f /etc/init .d /varnishd ]; then echo "Youcan'tconfigvarnish,Pleaseensureyouvarnishdirisornotexist..." exit 0 else count=` ps -ef| grep varnishd| grep - v grep | wc -l` if [$count- eq 0]; then echo "VarnishAlreadySuccessInstall,Nowstartvarnish...." cat <<EOF ------------------------------------------------------ StartVarnishtolisten0.0.0.0:80. TheVarnishloadbalancerserver1(192.168.149.1288080). TheVarnishloadbalancerserver1(192.168.149.1298080). TheVarnishMgraddresstolisten0.0.0.0:8001. TheVarnishCacheDIR /data/varnish/cache . EOF /usr/local/varnish/sbin/varnishd -n /data/varnish/cache -f /usr/local/varnish/etc/varnish/default .vcl-a0.0.0.0:80-s file , /data/varnish/varnish_cache .data,16G-puser=varnish-pgroup=varnish-pdefault_ttl=14400-pthread_pool_max=8000-psend_timeout=20-w5,51200,30-T0.0.0.0:8001-P /usr/local/varnish/var/varnish .pid if [$?==0]; then echo "Startvarnish...OK" fi else echo "Warning,ThisVarnishServiceisexist." fi fi } case $2 in install ) install ;; config) config ;; start) start ;; *) echo "Usage:{Usage$0versioninstall|config|start|help}" ;; esac Varnish简单测试如下图: 1.第一张图,第一次访问没有命中,去后端服务器取数据,同时在本地缓存一份:(MISS) 2.第二张图,第二次访问,缓存服务器存在,则直接从缓存中返回:(HIT) 实际线上环境中,如果用户访问我们的网站,使用Ctrl+F5刷新,我们的缓存就会失效,因为我们配置文件里面是这么配置的,匹配浏览器头信息: Pragma no-cache Cache-Control no-cache 1 2 3 4 if (req.http.Cache-Control~ "no-cache" ) { return (pass); } 只有注释掉这段代码或者设置只允许某个特定的IP,用户通过浏览器按Crtl+F5才不会把缓存给清除。 下面是针对某个特定的IP地址允许刷新: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 acl local { "192.168.149.128" } subvcl_hit{ if (!obj.cacheable){ return (pass); } if (client.ip~ local &&req.http.Pragma~ "no-cache" ){ set obj.ttl=0s; return (pass); } return (deliver); } 由于时间的原因,文章就暂时写到这里,更多深入的东西继续分享,文章引用煮酒哥的配置,非常感谢。欢迎大家一起讨论。 http://yuhongchun.blog.51cto.com/1604432/1293169 http://zyan.cc/post/313/ 本文转自 wgkgood 51CTO博客,原文链接:http://blog.51cto.com/wgkgood/1531694

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册