上述例子发现输出的结果是英文的,显然是不适合在国内环境使用,moment.js 提供了国际化支持,在现有的库中,moment 支持的语言可以说是相对完备了。通过引入对应的国际化资源(语言文件),来切换语言。<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/locale/zh-cn.min.js"></script><script> var now = moment().calendar(); console.log(now);// 输出当前日历时间 moment().startOf('hour').fromNow(); // 相对这个小时过去了多少分钟 var timestamp = 1593933593236; // 2020年7曰5日下午15点20分38秒 moment(timestamp).fromNow(); // 相对时间戳多久前</script>有关国际化的更多内容可以参考文档。
国际化的功能离不开错误码的支持,客户端指定语言到服务端去请求,当出错了服务端会根据错误码和语言找到对应的国际化提示语。从上面图中我们发现,错误码不仅仅是客户端与服务端的交互,后台各个服务间的交互也需要约定的一套错误码。一般一个系统的错误码 code 都是唯一确定的。msg 不同场景下可能不一样,提供给用户的肯定是需要友好且不能暴露底层细节,给开发人员看的就要详细专业的错误内容。网关服务上面维护着多套不同语言的错误码提示语,响应的时候会根据客户端带的 Lang 信息进行国际化转译。模块模块编码错误编码底层描述中文提示语英文提示语库存10001商品规格表关联有误商品不存在goods don’t exist一般国际化的系统中会有多份 xxx_lang.properties文件,每一份代表一种语言的消息提示语。中文一般会转为 Unicode 编码进行存储(这个过程一般开发工具可以设置自动转),这样的处理可以规避不同开发环境下不同编码导致中文乱码。
uni-app 的 API 与微信小程序 API 基本一致。掌握微信小程序 API 对后面的开发很有帮助。微信小程序 API 文档:https://developers.weixin.qq.com/miniprogram/dev/api/
注册微信小程序账号,获取到 AppID,我们后面配置的时候会用到。在 HBuilderX 工具栏,点击发行,选择小程序-微信。输入小程序名称和 AppID,单击发行就可以了。这样我们就会获得一个微信小程序的打包文件,接下来我们来发布微信小程序项目,打开微信小程序开发者工具,导入刚刚生成的微信小程序项目的打包文件,在微信小程序开发者工具中先测试一下,项目运行是否正常,项目测试没有问题后,点击右上角>>按钮,上传代码就可以发布微信小程序了,最后等待微信团队审核通过,别人就可以在线上访问到你的项目了。
pip 工具会从网站自动下载 Python 的第三方模块,提供下载 Python 第三方模块的网站被称为源。默认情况下,pip 从国外网站下载 Python 的第三方模块,速度非常的慢。为了加快下载速度,可以将 pip 的源改为国内的镜像源。国内常用的源如下:源的提供方源的 URL阿里云https://mirrors.aliyun.com/pypi/simple清华https://pypi.tuna.tsinghua.edu.cn/simple中国科技大学https://pypi.mirrors.ustc.edu.cn/simple华中理工大学http://pypi.hustunique.com豆瓣http://pypi.douban.com/simple
其实真实的原因是因为雪碧的英文名字是Sprite,而雪碧图的英文名是CSS Sprites,他俩同样都有Sprite这个词。左边是雪碧在国内的 Logo,右边是国外的 Logo。可以看到本来在没进入中国市场之前人家就叫Sprite(精灵),只不过进入了中国市场之后,中国区的负责人表示:用户去商店买汽水的时候如果对老板说:老板你这里有没有精灵?听起来就会感觉很奇怪,所以咱们不要被Sprite这个单词给限制住了,咱们应该起一个更符合中文语义的名字,于是乎“雪碧”诞生了。而雪碧图在国外叫做 CSS Sprite(CSS精灵)反正 Sprite 在国内被叫雪碧,那干脆不如咱们就叫它雪碧图吧!——来自最先接触到这项技术的人。所以后来接触这项技术的人也就跟随前人一起叫它雪碧图了。
通过配置文件,设置数据源信息。由于我们不再使用默认数据源,所以此处需要指定数据源类型为 DruidDataSource 。实例:# 指定数据源类型spring.datasource.type=com.alibaba.druid.pool.DruidDataSource# 配置数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 配置数据库urlspring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC# 配置数据库用户名spring.datasource.username=root# 配置数据库密码spring.datasource.password=Easy@0122
Flask 是一个 Python 实现的 Web 开发微框架,但是这个“微”并不代表着 Flask 功能比较简陋、有所欠缺。微框架中的 “微” 意味着:Flask 旨在保持核心简单而易于扩展;Flask 不会替用户做出太多决策,比如使用何种数据库;Flask 的选项(比如使用何种模板引擎) 通常有多个,用户很容易替换。默认情况下,Flask 不包含数据库抽象层、模板引擎、身份认证或其它任何已有多种库可以胜任的功能,如下图所示。然而,Flask 支持用扩展来给应用添加这些功能,应用程序可以很方便的集成这些扩展。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。
我们只需要通过配置文件指定数据源信息, Spring Boot 就可以识别配置,并加载到数据源组件中。 JdbcTemplate 也可以自动识别该数据源,从而实现对数据库的操作。配置文件信息如下:实例:# 配置数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 配置数据库urlspring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8# 配置数据库用户名spring.datasource.username=root# 配置数据库密码spring.datasource.password=123456需要注意的是,我们在 URL 配置中指定了编码方式,这样可以防止出现数据库中文乱码情况。同时指定了时区为北京时间所在的东八区(GMT%2B8),避免因时区问题导致错误。此时再次启动 Spring Boot 应用,正常运行,说明我们的数据源配置生效了。
微前端 尚处在发展时期,其核心概念和 微服务 相似。现阶段较为常用的微前端框架为 single-spa 和 qiankun,后者是基于前者实现的。该技术能做到 技术栈无关,即一个应用,能由多个不同技术的子应用构成,同时做到子应用的相互隔离,这里的隔离就可以选择采用 Web Components 实现。
本节我们讨论了如何利用第三方平台做我们系统的 OAuth2.0 认证中心,主要的知识点如下:Spring Security 实现了 OAuth2.0 客户端的常用功能,开发者可以轻松的将 Spring Boot 应用作为 OAuth2.0 客户端集成到安全体系中;在使用第三方作为 OAuth2.0 认证服务器时,首先要在第三方平台上完成应用注册,并获取到 Client ID 和 Client Secret 两个重要参数;使用第三方 OAuth2.0 认证源,可以简化系统开发中的关于认证的操作,并且可以更轻易的实现单点登录;使用第三方 OAuth2.0 认证源的时候,用户在本系统内的权限、详细用户信息等,仍需要在本地系统内维护;目前在国内支持度比较好的第三方认证源有:QQ、微信、微博、Github 等。下一节中,我们继续在 OAuth2.0 协议的基础上,构造出属于自己的认证中心。
上面介绍了一些 Scrapy Shell 和 Response 的基础知识,我们现在就来在 Scrapy Shell 中实战 Selector 选择器。本次测试的网站为广州链家,测试页面为二手房页面:链家二手房网站我已经在上面标出了想要爬取的网页信息,后面也主要测试这些数据的 xpath 表达式 或者 css 表达式。首先使用 scrapy shell 目标网址 命令进行想要的命令行,此时 Scrapy 框架已经为我们将目标网站的网页数据爬取了下来:(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/...>>> response<200 https://gz.lianjia.com/ershoufang/>我们看到响应的网页数据已经有了,接下来我们就可以开始进行网页分析来抓取图片中标记的数据了。首先是标题信息:提取二手房数据的标题信息根据上面的网页结构,可以很快得到标题的 xpath 路径表达式:标题://ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()在 Scrapy Shell 中我们实战一把:>>> response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')[<Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='地铁口 总价低 精装实用小两房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='刚需精装小三房/三房两厅一厨一卫/广州东绿湖国际城'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='周门社区 绿雅苑六楼 精装三房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='金碧领秀国际 精装修一房 中楼层采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='户型方正 采光好 通风透气 小区安静'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='毛纺小区 南向两房 方正实用 采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='南奥叠层复式 前后楼距开阔 南北对流通风好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='丹桂园 实用三房精装修 南向户型 拧包入住'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='周门小区大院管理 近地铁总价低全明正规一房一厅'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='云鹤北街 精装低楼层 南向两房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='中海誉城东南向两房,住家安静,无抵押交易快'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='精装两房,步梯中层,总价低,交通方便,配套齐全'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='中层南向四房 格局方正 楼层适中'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='精装修 户型好 中空一房 采光保养很好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='业主急售,价格优质看房方便有密码'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='汇侨新城北区 精装三房 看花园 户型靓'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='小区中间,安静,前无遮挡,视野宽阔,望别墅花园'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='小区侧边位 通风采光好 小区管理 装修保养好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='万科三房 南北对流 中高层采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='南北对流,采光充足,配套设施完善,交通便利'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='美力倚睛居3房南有精装修拎包入住'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='美心翠拥华庭二期 3室2厅 228万'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='天河公园门口 交通便利 配套成熟'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='保利香槟花园 高层视野好 保养很好 居家感好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='恒安大厦两房,有公交,交通方便,价格方面可谈'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='此房是商品房,低层,南北对流,全明屋'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='水荫直街 原装电梯户型 方正三房格局'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='嘉诚国际公寓 可明火 正规一房一厅'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='近地铁两房 均价低 业主自住 装修保养好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='宏城汇 三房 南向 采光好 户型方正 交通便利'>]上面结果返回的是 SelectorList 类型:>>> data_list = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')>>> type(data_list)<class 'scrapy.selector.unified.SelectorList'>最后我们通过提取 Selector 的 root 属性,只得到相应的文本信息:>>> data = [d.root for d in data_list]>>> data['地铁口 总价低 精装实用小两房', '刚需精装小三房/三房两厅一厨一卫/广州东绿湖国际城', '周门社区 绿雅苑六楼 精装三房', '金碧领秀国际 精装修一房 中楼层采光好', '户型方正 采光好 通风透气 小区安静', '毛纺小区 南向两房 方正实用 采光好', '南奥叠层复式 前后楼距开阔 南北对流通风好', '丹桂园 实用三房精装修 南向户型 拧包入住', '周门小区大院管理 近地铁总价低全明正规一房一厅', '云鹤北街 精装低楼层 南向两房', '中海誉城东南向两房,住家安静,无抵押交易快', '精装两房,步梯中层,总价低,交通方便,配套齐全', '中层南向四房 格局方正 楼层适中', '精装修 户型好 中空一房 采光保养很好', '业主急售,价格优质看房方便有密码', '汇侨新城北区 精装三房 看花园 户型靓', '小区中间,安静,前无遮挡,视野宽阔,望别墅花园', '小区侧边位 通风采光好 小区管理 装修保养好', '万科三房 南北对流 中高层采光好', '南北对流,采光充足,配套设施完善,交通便利', '美力倚睛居3房南有精装修拎包入住', '美心翠拥华庭二期 3室2厅 228万', '天河公园门口 交通便利 配套成熟', '保利香槟花园 高层视野好 保养很好 居家感好', '恒安大厦两房,有公交,交通方便,价格方面可谈', '此房是商品房,低层,南北对流,全明屋', '水荫直街 原装电梯户型 方正三房格局', '嘉诚国际公寓 可明火 正规一房一厅', '近地铁两房 均价低 业主自住 装修保养好', '宏城汇 三房 南向 采光好 户型方正 交通便利']是不是非常简单就爬到了数据?另外,我们还可以使用 extract()[0] 或者 extract_first() 这样的方式来提取结果列表中的第一个文本数据:>>> data = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract()[0]>>> data2 = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract_first()>>> data == data2True>>> data'地铁口 总价低 精装实用小两房'接下来,我们依次找出获取二手房位置、房屋价格、房屋信息的 xpath 路径表达式:房屋位置://ul[@class="sellListContent"]/li/div/div[@class="food"]/div[@class="positionInfo"]/a[]/text()房屋信息://ul[@class="sellListContent"]/li/div/div[@class="address"]/div[@class="houseInfo"]/text()房屋价格://ul[@class="sellListContent"]/li/div/div[@class="priceInfo"]/div[@class="totalPrice"]/span/text()有了这些之后,我们就可以依次提取出二手房的【标题介绍】、【房屋位置】、【房屋信息】以及【房屋价格】这些信息。此外对于提取的【房屋信息】字段还要进一步处理,分割成【房屋结构】、【房屋大小】以及【朝向】等信息。这些信息将在 Spider 模块中进行提取,也就是我们前面互动出版网爬虫的 ChinaPubCrawler.py 文件中的 ChinaPubCrawler 类来解析。最后我们在介绍下 scrapy shell 命令的参数:(scrapy-test) [root@server ~]# scrapy shell --helpUsage===== scrapy shell [url|file]Interactive console for scraping the given url or file. Use ./file.html syntaxor full path for local file.Options=======--help, -h show this help message and exit-c CODE evaluate the code in the shell, print the result and exit--spider=SPIDER use this spider--no-redirect do not handle HTTP 3xx status codes and print response as-isGlobal Options----------------logfile=FILE log file. if omitted stderr will be used--loglevel=LEVEL, -L LEVEL log level (default: DEBUG)--nolog disable logging completely--profile=FILE write python cProfile stats to FILE--pidfile=FILE write process ID to FILE--set=NAME=VALUE, -s NAME=VALUE set/override setting (may be repeated)--pdb enable pdb on failure比较常用的有 --no-redirect 和 -s 选项:--no-redirect : 指的是不处理重定向,直接按照原始响应返回即可;-s:替换 settings.py 中的配置。常用的有设置 USER_AGENT 等。
可以通过 CDN 引入 ECharts 文件:<!-- bootstrap 服务 --><!-- bootstrap 提供的免费CDN服务,亲测非常稳定 --><script src="//cdn.bootcss.com/echarts/4.5.0/echarts.common.js"></script><!-- 七牛云存储服务 --><!-- 国内速度稳定,开放性强 --><script src="//cdn.staticfile.org/echarts/4.5.0/echarts.common.js"></script><!-- jsdeliver 服务 --><!-- 微软的CDN服务,虽然国内访问速度比不上国内CDN,但速度不至于太慢,有国际化需求的可以试试 --><script src="//cdn.jsdelivr.net/npm/echarts@4.5.0/echarts.common.js"></script><!-- cdnjs 服务 --><!-- 一个非常全的CDN服务,存储了大多数主流的js、css、图片库 --><script src="//cdnjs.cloudflare.com/ajax/libs/echarts/4.5.0/echarts.common.js"></script>
通过配置文件,设置数据源信息。实例:# 配置数据库驱动spring.datasource.driver-class-name=com.mysql.jdbc.Driver# 配置数据库urlspring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC# 配置数据库用户名spring.datasource.username=root# 配置数据库密码spring.datasource.password=Easy@0122
1. 独立的页面维护了全局 错误码错误码由5位整数构成2. 每个接口一个独立的 参数说明页面正常情况下出参只返回业务实体异常情况才有 errCode errMsg每个接口下也可能有自己的业务错误码
咱们打开新浪微博,然后随便找个带皇冠的,按下F12键(Mac用户按command+option+i)然后选中控制台的箭头,再点击皇冠:再点开这个图片可以发现:可以看到那些大 V 图标皇冠图标以及各种微博认证等图标,都是放在了一张雪碧图里(即使再牛的大 V,身份标志也是放在雪碧图中的)。
1969年,IBM 的技术人员 Charles Goldfarh 和 Edward Mosher 等人一起发明了通用标记语言 GML(Generalized Marked Language)。1985年在英国成立了国际 SGML 用户组织,在1986年,SGML 成为国际标准ISO8879:信息处理标准通用标记语言(Information processing Text and office system Standard generalized markup language)。HTML 和 XML 派生于 SGML,XML 可以被认为是它的一个子集,而 HTML 是它的一个应用。为了告诉浏览器我们需要展示什么内容,HTML 定义了一整套符号标记规范,这些规范包括设置文字的格式;创建列表;显示图片;显示多媒体;显示超链接;等等。
事件日志中包含屏幕电源状态,其中 0 表示屏幕关闭,1 表示屏幕打开,2 表示已锁屏。日志范例如下:grep screen_toggled bugreport-2015-10-18-16-52-22.txt10-18 15:05:04.383 992 992 I screen_toggled: 110-18 15:05:07.010 992 992 I screen_toggled: 010-18 15:23:15.063 992 992 I screen_toggled: 110-18 15:23:25.684 992 992 I screen_toggled: 010-18 15:36:31.623 992 992 I screen_toggled: 110-18 15:36:37.660 3283 3283 I screen_toggled: 2错误报告中还包含关于唤醒锁的统计信息,唤醒锁是应用开发者采用的一种机制,用于表明其应用需要设备保持开启状态。唤醒锁总时长统计信息仅跟踪唤醒锁实际负责使设备保持唤醒状态的时间,不包括屏幕处于开启状态的时间。此外,如果同时持有多个唤醒锁,系统会在它们之间分配唤醒锁时长。
gem push my_gem_duxiao-0.1.0.gem命令行显示:Enter your RubyGems.org credentials.Don't have an account yet? Create one at https://rubygems.org/sign_up Email:然后根据提示输入刚刚注册的 rubygems.org 用户名和密码就可以了。上传完成之后,我们就可以在Gemfile里面使用gem 'my_gem_duxiao'来使用了!之后我们可以将我们刚刚制作的的Gem给放到Github上进行版本管理,这里就不再赘述。Tips:为了RubyGems社区的整洁,大家尽量不要发布一些没有用的Gems到线上去。经验:我们使用 Gemfile 来下载 Gem 的时候,尽量不要使用国外的 Gem 源,而使用国内的,这样下载速度会快很多,当然您如果在国外的服务器上使用 Gemfile,也要尽量使用国外的 Gem 源。source 'https://rubygems.org/' # 修改source为:https://gems.ruby-china.com/gem 'rails', '4.2.5'...国内的Gem源:https://gems.ruby-china.com/国外的Gem源:https://rubygems.org/修改 Gem 源的链接。
决策树其实很简单,我们日常生活中用到的例子非常多,举个简单的分类的例子:我中午饿了,要去吃饭,学校里面一共有两个餐厅,而我一共有4个选择:A餐厅:价格贵;B餐厅:价格便宜;吃泡面:不用出宿舍,前提是宿舍售卖机有卖;点外卖:不用出宿舍。那么,我们的决策树就如下图所示:如果我想出门而且有钱,我就去 A 餐厅;如果我想出门但是却钱不多,那么我就去 B 餐厅;倘若我不想出宿舍而刚刚好宿舍有卖泡面,我就可以直接吃泡面;到那时如果没有泡面的话,我就只能点外卖。这只是一个简单的理解,而具体的优化的细节涉及到许多的数学原理,这里就不过多解释了。
什么是标准?标准是对重复性事物和概念所做的统一规定,他以科学技术和实践经验的结合成果为基础,经有关方面协商一致,由主管机构批准,以特定形式发布作为共同遵守的准则和依据。在这个标准概念大行其道的今天,如果大家在生活中稍微注意一下,其实可以很容易发现,我们生活在一个标准化的世界里,每个产品的外包装或者标签上都会注明相应的执行标准。标准贯穿着我们的衣食住行。。总而言之,标准无处不在。当然标准具有行业性与地域性。每个国家基本都有自己的标准,这些标准有些是自己独立创造的,但更多的,为了与国际上大部分标准兼容,基本上会使用国际上一些通用的标准,只是改一下名字就可以了。作为软件行业中的老大哥级人物。C 语言也有自己的标准,而且是国际标准。如果大家遵循同一个标准,那么就会避免分歧的产生,避免出现很多不必要的麻烦和困扰。按照这统一标准就不会出现不兼容的问题。我们先来看下 C 语言标准的发展历史:
安全密钥交换与非对称密钥算法,推动了网络安全通信的发展,在认证与安全诉求越来越迫切的环境下,一些厂商嗅探到市场的机遇,逐渐推出了相应的认证服务。信息的安全关系到国家安全和经济利益,因此我国这几年也在积极探索网络安全的标准化建设,这些标准并不仅仅局限于证书的签名算法,也包括了物理厂房的建设,人员的管理流程规则制度。当然我们的网络安全建设一方面要保证自己足够安全,另一方面也要对接国际,毕竟今天的网络今天的经济都是全世界共同的。
信号量隔离和线程池隔离的方式很相似,只不过把分配线程池的方式改为了分配信号量(至于什么是信号量,请同学们自行查阅)。在处理请求时,Hystrix 会分配一个信号量的阀值,当服务接收到一个请求后,信号量的阀值减 1 ,当请求处理完毕后,信号量的阀值加 1,当信号量的阀值减为 0 时,则不再接收请求,即该请求会被拒绝处理。@RequestMapping(value = "hello", method = RequestMethod.GET)@HystrixCommand(fallbackMethod="helloFail", commandProperties = {@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "100")})@ResponseBodypublic String hello() throws InterruptedException { return "helloWorld";}public String helloFail() { return "helloFailed";}代码解释:第 4 行,通过指定 HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY 参数的值为 SEMAPHORE ,来声明该接口使用信号量隔离。第 7 行,通过指定 HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS 参数的值为 100 ,可以理解为设置信号量的阀值为 100 。通过添加上述配置参数,我们就可以通过信号量隔离的方式来实现服务资源隔离。Tips: 一定要合理设置信号量的阀值,不要随意设定,如果阀值设置过大,则请求不会停止,如果阀值设置过小,则不能满足业务需要。
我们打完包之后如何查看签名信息呢?有以下 2 种方法:方法一:其实 Gradle 默认给我们内置了很多任务,里面就包含查看应用签名的任务 signingReport。我们执行这个任务就可以看到应用的签名了。方法二:第二种方法就是直接使用命令查看签名信息,我们在控制台输入下面的命令keytool -list -v -keystore "[签名文件路径]"就可以看到签名信息了,包括我们之前写的组织/公司,省份,国家等。 Tips: 我们对于签名文件的注意点就只有下面一点,设置签名的 key 和密码时不要太过复杂,不然在看签名信息的时候,输入密钥库口令时看不到输入的内容,很容易输入错误。
go mod 虽然可以直接帮我们下载好我们需要的包,但是因为 Go 语言很多包都是在国外的服务器上,国内下载具有一定的困难。但是不用担心,go mod 提供了代理服务,同时很多国内的云服务器产商都提供了自己的代理服务:阿里云: https://mirrors.aliyun.com/goproxy 微软: https://goproxy.io 七牛云: https://goproxy.cn GoCenter: https://gocenter.io
首先在项目初期,测试人员提前介入,进行接口测试,模拟客户端与服务端的交互。有问题提前抛出来。保证接口调用是没问题的。其次,接口测试完成之后再进行系统测试,会轻松很多。明白了各个接口在做什么,各个参数的模拟实际上就是业务场景的模拟。系统测试出现一些问题可以更快速的定位是客户端还是服务端出问题。另外,接口是获取和操作资源的方式,而大部分系统和产品中,资源一般都是产品的核心,比如微信核心资源就是通讯录关系链和聊天记录等,因此资源是必测的。而接口中大部分的内容是数据,通过数据的对比我们能推测到系统和产品的逻辑,测接口就是测逻辑。最后接口中的返回相对单纯,不像 web 页面,HTML 代码中有太多 UI 的东西,UI 最不稳定,变化太快,接口相对稳定一点点,但是里面的干扰信息更少,断言相对容易很多。
Zabbix是一个企业级的高度集成开源监控软件,提供分布式监控解决方案,可以用来监控设备、服务等的可用性和性能,由国外团队进行维护以及持续更新源码,可以自由下载,是一个真正的源代码开放产品。Zabbix的通用架构是C/S架构,通过B/S在web端进行展示和配置,分布式架构为Client/Proxy/Server,Zabbix-Server将采集到的数据持久地存储到数据库中。Zabbix数据的采集不仅可以使用Agent方式,也可以使用SNMP、SSH、Telent、IPMI等多种协议。Zabbix的主要特点:安装配置简单;免费开源,试错成本低;支持多语言;自动发现服务器;分布式监控;集中式web管理;邮件、微信等通知功能。Zabbix 的新版本 5.0,可以支持 proxy/server 端通过 odbc 连接 MySQL 进行采集监控数据,所有的配置在web端都可以完成,而且有现成的完善的监控模板,使用起来非常简单高效。
首先要理解数据源的作用,数据源实际上是一个接口 javax.sql.DataSource 。 Spring Boot 在操作数据库时,是通过数据源类型的组件实现的。数据源的类型有很多,但是都实现了 javax.sql.DataSource 接口,所以 Spring Boot 可以尽情地更换各类数据源以实现不同的需求。其中 Druid 数据源就是数据源中比较出类拔萃的存在,而且是阿里开发的。国人出品的东西,咱们能支持的必须得支持啊,当然这是建立在人家做的确实好的基础上。 Druid 是阿里巴巴数据库事业部出品本篇我们使用 Druid 替换默认的数据源,然后做一下性能对比测试。网上有很多文章写 Druid 性能如何如何强悍,但是很多并没有事实依据。我们做程序员还是要严谨,相信实践是检验真理的唯一标准。所以本篇的内容就是研究下 Druid 如何使用,及其性能到底是否足够优异。
业务场景描述有这样一个真实的业务场景:在某大厂某销售业务项目中,由于某大厂销售业务板块业务的持续增加,导致之前原本设计好的项目架构出现了问题,不足以支撑持续增长的业务需要,于是,某大厂程序员对项目架构做了拆分,并最终形成了以 Spring Cloud 为基础架构的微服务分布式项目架构。在拆分了项目架构之后,虽然可以支撑持续增长的业务需要,但是,在拆分后的项目架构中,Hystrix 无法对所有项目进行监控,即 Hystrix 服务监控平台只能监控一个分散的项目,无法对项目整体进行监控。问题原因分析在解决问题之前,我们首先来分析一下这种问题产生的原因。上述场景场景中,项目的架构方式是微服务的分布式架构,而一般来说的 Hystrix 微服务监控平台默认只对一个项目实例起作用,所以,也就导致了一个微服务平台只对一个微服务实例起作用。
熟悉 CentOS 操作系统的人都知道,CentOS 中默认的软件包管理工具是 Yum ,安装某个软件,我们只需要输入如下命令即可: $ sudo yum install 软件包使用 Yum 工具的好处就是,它能自动帮我们处理依赖关系,连同依赖包一起下载安装。由于默认的是使用的国外的源,往往为了加快下载速度,我们会选择使用国内的 Yum 源。比较有名的有清华源、阿里源、163源等等。这些网站提供的软件包都是凌晨从国外的最全的 Yum 源地址同步,然后更新索引,提供给国内用户免费使用。往往在生产环境上部署服务时,为了安全性,除了一台虚拟机能联通外网外,其余机器都处于内网环境,是无法联通外界网络的,为了在这些机器上能统一安装某些服务,我们还需要在内网构建自己的 Yum 源。构建 Yum 源的步骤非常简单,按照如下步骤走:准备好yum源包我们准备好自己的rpm包,这些包可以通过下面的方式获取: $ sudo yum install nginx --downloadonly --downloaddir=./这样会将 Yum 源对应的 nginx包以及相关依赖下载下来,但并不安装。我们要的就是所有这些 rpm 包。按照上面的方式,下载我们所有想要安装软件的 rpm 包,然后将其全部放入到目录 yum_source 下,最后连同包和目录压缩成 yum_source.zip 文件。上传到某个服务器上传到 ceph1 的 /data 目录(可以是任意地址)下,并解压,又重新得到 yum_source 目录,目录下全是我们需要的 rpm 包,可以用目录将这些 rpm 包分类,比如 ansible及其依赖包就放入 /data/yum_source/ansible目录下,这样看起来会比较清晰,而不是所有的包都混在同一个目录下。这样,我们的 yum 源其实就构建好了,源地址就是 /data/yum_source。构建索引接下来,我们使用 createrepo 这个命令来构建 yum 源的索引。这个命令需要单独安装,所以首先需要找一台能联网的机器,将 createrepo 所依赖的 rpm 下载下来,然后将其放入到ceph1的 /data/yum_source/createrepo 目录下。然后我们进入 /data/yum_source/createrepo 目录,直接使用yum localinstall createrepo-xxx.noarch.rpm即可安装该命令。# 如果无权限进入,可以改下权限,或者后面得指令使用绝对路径$ cd /data/yum_source/createrepo/# 根据下载的包对应安装$ sudo yum localinstall createrepo-0.9.9-28.el7.noarch.rpm# 如果没有找到当前目录下的依赖,可以直接用rpm -ivh安装这个目录下的所有依赖包$ sudo rpm -ivh *.rpm# 有了createrepo命令,我们用-v参数来对yum源目录建立索引$ sudo createrepo -v /data/yum_source下图中的 repodata 就是 createrepo 命令给整个目录生成的索引文件。Nginx作为静态资源服务器万事俱备,只差 Nginx 作为静态资源服务器来让我们访问到 yum 源中的资源。这对 Nginx 来说就是轻而易举的事情。我们按照第6节的内容安装 Nginx ,修改 Nginx.conf 配置文件,内容如下:$ 进入nginx的配置文件目录$ cat nginx.confuser root;# 根据探测机器cpu核数设置worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 8089; server_name localhost; root /data/yum_source; autoindex on; index index.html index.htm; }}最核心的地方,就是我们加了一个监听8089的端口配置,将8089端口过来的请求转向 /data/yum_source 目录下的静态文件。我们本地访问这个主机的8089端口,如下图所示:那么如何让 ceph2 机器使用这个 yum 源呢?很简单,只需要将 ceph2 的 /etc/yum.repos.d/ 目录下的所有 repo 文件备份保存到其他位置,然后新建一个 repo 文件,将里面的地址指向这个 ceph1 即可。具体操作参考下面的 shell 命令:$ mkdir /etc/yum.repos.d/bak$ mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak# 新建internal.repo文件,并写入yum源地址$ vim /etc/yum.repos.d/internal.repo$ cat /etc/yum.repos.d/internal.repo[Local-Yum]name=internal yumbaseurl=http://172.16.0.8:8089/enabled=1priority=1gpgcheck=0gpgkey=http://172.16.0.8:8089/release.asc注意这里我们将源名称命名为Local-Yum,接下来我们在 ceph2 上使用 yum 命令安装某个服务(要求 ceph1 中必须要有该软件的 rpm 包):$ yum clean all $ yum install nginx 从下图中,可以看到我们的内部源确实生效了,使用的是我们定义的内部源 Local-Yum 。此外使用内网源可以大大加快下载软件的速度,提升工作效率。