Docker容器跨主机通讯的几种方式 前言:Docker的5种网络模式BridgeHostNoneContainerNetwork 1. 直接路由方式2. 基于OVS+GRE的大二层通信 前言:Docker的5种网络模式 Bridge 此时docker引擎会创建一个veth对,一端连接到容器实例并命名为eth0,另一端连接到指定的网桥中(比如docker0),因此同在一个主机的容器实例由于连接在同一个网桥中,它们能够互相通信。容器创建时还会自动创建一条SNAT规则,用于容器与外部通信时,类似家里上网用的ISP提供给我们的动态IP。如果用户使用了-p或者-P端口,还会创建对应的端口映射规则,使得外部请求能够访问容器的服务,但是你不能通过IP直接访问,本文提供了3种方式实现容器的跨主机访问。
Host 与宿主机共享网络,此时容器没有使用网络的namespace,宿主机的所有设备,会暴露到容器中,因此存在安全隐患。
None 不设置网络,相当于容器内没有配置网卡,用户可以手动配置。
Container 指定与某个容器实例共享网络
Network 使用自定义网络,可以使用docker network create创建,并且默认支持多种网络驱动,用户可以自由创建桥接网络或者overlay网络。
Prework:
安装Docker安装OVS(非编译)
apt-get install openvswitch-switch编译安装OVS: apt-get install git apt-get install autoconf automake libtool apt-get install openssl apt-get install libssl-dev apt-get install make apt-get install make-guile apt-get install python-six 获取代码:
git clone https://github.com/openvswitch/ovs.git
编译操作:
cd ovs ./boot.sh ./configure make make install 加载模块
/sbin/modprobe openvswitch
查看安装是否成功:
/sbin/lsmod | grep openvswitch
修改环境变量:
export PATH=$PATH:/usr/local/share/openvswitch/scripts
练习SQL语句大全(关键看思路) 该数据有三张表:
部门表dept(deptno部门编号、dname部门名称、loc位置)
/* Navicat MySQL Data Transfer Source Server : localhost_3306 Source Server Version : 50718 Source Host : localhost:3306 Source Database : sql_demo Target Server Type : MYSQL Target Server Version : 50718 File Encoding : 65001 Date: 2019-02-23 21:54:13 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for dept -- ---------------------------- DROP TABLE IF EXISTS `dept`; CREATE TABLE `dept` ( `deptno` int(10) NOT NULL COMMENT '部门编号', `dname` varchar(14) DEFAULT NULL COMMENT '部门名称', `loc` varchar(13) DEFAULT NULL COMMENT '部门位置', PRIMARY KEY (`deptno`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of dept -- ---------------------------- INSERT INTO `dept` VALUES ('10', '后勤部门', 'NEW YORK'); INSERT INTO `dept` VALUES ('20', '开发部门', 'DALLAS'); INSERT INTO `dept` VALUES ('30', '销售部门', 'CHICAGO'); INSERT INTO `dept` VALUES ('40', '推广部门', 'BOSTON'); 员工表emp(empno工号、ename员工姓名、job职位、mgr直属领导工号、hiredate入职日期、sal月薪、comm补贴、deptno部门编号)
title: Go语言解析时间并计算时间差
tags: go
计算时间差 在项目开发中,计算时间差是个很常用的功能, 比如说我们开发一个倒计时应用,要计算今日距离某一天相差多少时间。
使用Sub函数 Sub()两个时间点相减,获得时间差。
t1:=time.Now() //获取本地现在时间 time.Sleep(time.Second*2) //延时2秒 t2:=time.Now() d:=t2.Sub(t1) //两个时间相减 fmt.Println(d) 字符串转时间 Parse()可以帮我解析字符为时间
Time, _:= time.Parse("2006-01-02 15:04:05", "2018-10-15 22:00:00") TimeNow := time.Now() left := Time.Sub(TimeNow) fmt.Println(left) //8h55m42.543575s 此处结果只做演示,不同时间运行结果不同 fmt.Println(left.Hours()) //获取小时 输出 8 fmt.Println(left.Minutes()) //获取分钟 输出 55 fmt.Println(left.Seconds()) //获取秒 输出 42... 当如如果真的用Parse解析时间就坑爹了,因为它会把字符串解析成国际标准时间,即Parse会将时区设置为UTC。
时间戳转字符串 TimeNow:= time.Now() UnixTime:=TimeNow.Unix() dataTimeStr := time.Unix(UnixTime, 0).Format("2006-01-02 15:04:05" ) //设置时间戳 使用模板格式化为日期字符串 fmt.Println(dataTimeStr) //2018-10-15 20:40:43 当然 我们使用time.Now().String() 也是可以的,但是这样输出的字符串并不简洁,如2018-10-15 20:40:43.894491 +0800 CST m=+0.001191386
原文地址:DMSP卫星介绍和数据下载 http://blog.sina.com.cn/s/blog_764b1e9d0100y2bl.html 作者:ENVI-IDL技术殿堂
DMSP(Defense Meteorological Sate-llite Program)是美国国防部的极轨卫星计划,与NOAA卫星同属于一类,只不过星上载荷不同。
现有DMSP为三轴姿态稳定卫星,运行在高度约830km的太阳同步轨道,周期约101min,扫描条带宽度3 000km。DMSP卫星采用双星运行体制,两颗业务卫星同时运行,过赤道时间为05∶36及10∶52,每6h可提供一次全球云图。
该计划自1965年1月19日发射第1颗卫星,至今共发射7代40多颗卫星。2009年10月18日发射的是DMSP F-18 Block 5D-3,后续将发射2颗,即DMSP 5D-3F19~5D-3F20。
表1 已发射过的DMSP(截至2009年底)
卫星划分
卫星型号
发射时间
状况
第1代
DMSP 4AF1~F10-125
1951965-01-19~1967-10-11
退役
第2代
DMSP 5AF1~F6
1951968-05-23~1971-02-17
退役
第3代
DMSP 5BF1~F6
1951971-10-14~1974-08-09
退役
第4代
DMSP 5CF1~F2-175
1951975-05-24~1976-02-19
退役
第5代
DMSP 5D-1F1~F5-450
5131976-09-11~1999-12-12
退役
第6代
DMSP 5D-2F6~F14-750~830
1982-12-21~1997-04-04
部分在轨
第7代
DMSP 5D-3F15
1999-12-12
在轨
DMSP 5D-3F16
2001-01-19
在轨
DMSP 5D-3F17
2006-11-04
在轨
DMSP 5D-3F18
2009-10-18
在轨
DMSP卫星的发射目的:
·云图监测,以获得云的分类信息;
标签
说明
div
文档节 块 区域
ul
无序列表
ol
有序列表
dl
定义列表
p
段落
h1-h6
标题
table
表格
form
表单
之前遇到一个需求,字段是每小时那种(2018101208)。然后服务给我数据少了部分。然后我需要自已推算。然后补上没有那些小时为单位的数据。这里面也涉及到了自已设计出来数据list是全面的。所有的小时都有。然后把服务器获取list根据这个进行对比,补上服务器没有的小时数。两个list根据某个字段进行合并。
1、获取过去15天的小时 初始化变量
public String start_ts = "1537812000000"; //解析出来历史数据 private List<HistoryData> historyDataList = new ArrayList<>(); //自已推算的历史数据 private List<String> calculateDataList = new ArrayList<>(); 获取系统当前时间,推算15天前这个时间,然后把每小时存在List<String>里面。这也是一个小小的算法。
public void SetContext(Context context) { if (mContext != null) mContext.clear(); mContext = new WeakReference<Context>(context); long time = System.currentTimeMillis(); SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHH"); String strNow = df.format(time); time = dateConvertMillionSec(strNow); //获取当前时间,减去24小时。就是获取过去一天每小时的数据 long tempTime = 1000 * 60 * 60 * 24 * 15L; start_ts = String.valueOf(time - tempTime); for (int j = 1; j < 15; j++) { for (int i = 1; i < 25; i++) { calculateDataList.
相比于OpenCV3.4的功能改进:
1.OpenCV DNN模块新增ONNX解析器,支持各种分类网络,比如AlexNet, Inception v2, Resnet, VGG 等。部分支持tiny YOLO v2 目标检测网络。
2.DNN模块新的改进
2.1)支持Mask RCNN并新增里对应例程!
2.2)使用Intel Inference Engine(Intel推断引擎),即Intel OpenVINO的一部分,使得目标检测更快!
2.3)改进OpenCL作为后端的稳定性。
3.快速的QR二维码检测,在Core i5 CPU 640*480分辨率的图像上可以达到约80FPS!后续OpenCV4.0正式版本将加入QR二维码的解码,使其成为完整的解决分方案。
4.通过所谓的“wide universal intrinsics”不断扩展SSE4,AVX2和NEON优化内核集,持续加速OpenCV!
另外,OpenCV4.0 alpha包含一些相比之前版本的独有特性:
1.C++ 11标准的支持(所以编译新版本需要C++11兼容的编译器)。所以许多非常nice的特性比如支持lambda函数的parallel_for,cv::Mat更加方便迭代访问,通过列出元素初始化cv::Mat都得到了默认支持。
2.标准的std::string和std::shared_ptr取代了原先版本的cv::String和cv::Ptr。 parallel_for现在可以使用std::threads线程池作为后端。
3.清理了OpenCV 1.x的旧版C API(CvMat,IplImage等),OpenCV4.0正式版将清理完成。
4.添加了基本类型16位浮点型(CV_16F ),这将对诸如对精度要求不高的比如深度学习的推断加速很明显。
5.CPU和GPU加速的KinFu实时三维密集重建算法添加到opencv_contrib中。
6.HPX加入并行计算的后端。
7.新的棋盘格(chessboard)探测器。
一、下载OpenCV4.0 直接到官网https://opencv.org/下载,直接就选择了最新版
然后根据自己的系统选择相应的选项。笔者这里选择的是win系统。
运行后指定位置可以得到相应的包以及文件里面各个子文件
二.配置环境变量 笔者是win10的系统,如果是其他系统,读者可自行查阅其他的系统属性,虽然可能会步骤不一样,但是最后都是对系统的环境变量进行编辑。
打开控制面板-》系统安全-》系统-》高级系统设置
然后打开环境变量
到了这一步,说明系统的环境变量已经完成了。
三.编译器的环境变量配置。 打开VS2017,新建一个C++的项目。
在项目里面新建一个cpp源文件
新建完C++的文件后,选择视图-》其他窗口-》属性管理器
然后可以得到下图:
这个与你编译的环境相关,我一般都是
所以我选择Debug+x64,选择右击
点击确定,接着在选择库目录
配置完成后点击确定,再选择链接器。
这个位置要注意,这里需要手动添加,并找到相应的位置,也是和opencv3.x不同的地方
在vc14-》lib,opencv_world400.lib和opencv_world400d.lib
在vc15-》lib也有同样的lib,链接器里面没有路径之分,所以笔者这里猜测应该填一个文件下的lib就行。
填完后,点击确定就行。
四.测试环节 #include <iostream> #include<opencv2/opencv.hpp> using namespace cv; int main() { //读取本地的一张图片便显示出来 Mat img = imread("
需要知道 String a = "abd"; 和String b = new String();的区别。 要明确每次只要new一次堆内存就会开辟一个新的内存地址。
可以使用intern()方法手动将new出来的对象丢到字符串常量池中。
public class StringDemo { public static void main(String[] args) { String str = "Hello"; str = str + "World"; str += "!!!"; System.out.println(str); } } public static void main(String[] args) { String stra = "hello" ; String strb = "hello" ; String strc = "hello" ; System.out.println(stra == strb);//true System.out.println(stra == strc);//true System.out.println(strb == strc);//true } } String e = "
Xpath (XML Path Language),是W3C定义的用来在XML文档中选择节点的语言
一:从根目录/开始
有点像Linux的文件查看,/代表根目录,一级一级的查找,直接子节点,相当于css_selector中的>号
/html/body/div/p 二. 根据元素属性选择:
查找具体的元素,必须在前面输入标准开头//,表示从当前节点寻找所有的后代元素
//div/* div下面的所有的元素
//div//p 先在整个文档里查找div,再在div里查找p节点(只要在内部,不限定是否紧跟) ;等价于 css_selector里的('div p')
//div/p p是div的直接子节点; 等价于 css_selector里的('div > p')
//*[@style] 查找所有包含style的所有元素,所有的属性要加@; 等价于 css_selector里的('*[style]')
//p[@spec='len'] 必须要加引号;等价于 css_selector里的("p[spec='len']")
//p[@id='kw'] xpath中对于id,class与其他元素一视同仁,没有其他的方法
三. 选择第几个节点
//div/p[2] 选择div下的第二个p节点 ;等价于css_selector里的div>p:nth-of-type(2) 符合p类型的第二个节点
//div/*[2] 选择div下第二个元素
//div/p[position()=2] position()=2 指定第二个位置; 等价于上面的 //div/p[2] position()>=2 位置大于等于2
position()<2 位置小于2
position()!=2 位置不等于2
//div/p[last()] 选择div下的倒数第一个p节点; last()倒数第一个
//div/p[last()-1] 选择div下的倒数第二个p节点;
//div/p[position()=last()] 倒数第一个
//div/p[position()=last()-1] 倒数第二个
//div/p[position()>=last()-2] 倒数第一个,第二个,第三个
四. 组合选择
//p | //button 选择所有的p和button,等价于css_selector里的 p, button
该工具类主要操作数组。
以下数组的类型使用T代替,同一函数中T代表相同类型。T可包含的类型有:Object、boolean、int、byte、char、double、float、long、short。
数组判断函数:
1.判断是否存在,返回boolean:
ArrayUtils.contains(T[] array, T objectToFind)
2.判断是否为空,返回boolean:
ArrayUtils.isEmpty(T[] array)
3.判断数组是否相同,返回boolean:
ArrayUtils.isEquals(T array1, T array2)
4.判断数组是否相同长度,并且长度不为0,返回boolean:
ArrayUtils.isSameLength(T[] array1, T[] array2)
数组增加函数:
1.添加指定元素到数组中,返回Array:
ArrayUtils.add(T[] array, T element)
2.添加指定元素到数组的index位置中,返回Array:
ArrayUtils.add(T[] array,Int index, T element)
3.合并两个数组,返回Array:
ArrayUtils.addAll(T[] array1, T[] array2)
4.复制数组,返回数组:
ArrayUtils.clone(T[] array)
数组移除函数:
1.移除指定位置的元素,返回Array:
ArrayUtils.removeElement(T[] array, int element)
2.移除指定元素,返回Array:
ArrayUtils.removeElement(T[] array, T element)
数组查找函数:
1.查找数组中是否存在,并返回其第一个位置,返回int,-1代表不存在:
ArrayUtils.indexOf(T[] array, T objectToFind)
2.查找数组中是否存在,并返回其最后一个位置,返回int,-1表示不存在:
ArrayUtils.lastIndexOf(T[] array, T objectToFind)
3.查找指定位置间的子数组,返回子数组Array:
ArrayUtils.subarray(T[] array, int startIndexInclusive, int endIndexExclusive)
本文主要记录了gcc共享库的制作和使用过程。 (1)命名规则
(a)lib+名字+.os
(2)制作步骤
(a)生成与位置无关的代码(.o文件)
gcc -fPIC -c *.c 共享库只记录相对位置。
(b)将.o打包成共享库(动态库)
gcc -shared -o libMyCalc.so *.o -Iinclude (3)发布和使用共享库
生成可执行文件方法一:
gcc main.c lib/libMyCalc.so -o app –Iinclude 方法二:
gcc main.c -Iinclude -Linclude -L./lib -lMyCalc -o myapp 查看可执行文件依赖的库:ldd myapp
这里的libMyCalc.so显示未找到,解决该问题请看我的博客《解决程序执行时动态库无法被加载的问题》https://blog.csdn.net/HuYingJie_1995/article/details/82985961
1. 错误信息如下,提示导入源文件jquery失败,有可能是导入路径错误或者没有导入jquery文件,请跳转到步骤2;
2. 查看html引入jquery源文件路径(如化红线所示),多了一斜杆"/",正确引入路径为:<script src="jquery/jquery-3.3.1.min.js"></script>
3. jquery项目目录如下图:
今天打开博客的时候,发现网站所有引用 bootCDN的资源 全部无法加载,并且打开bootCDN搜索的资源地址都指向了cdnjs.cloudflare.com,难道是遭受攻击了?但是小编通过查阅官方博客,发现最新的公告是在2017年9月30日,那次公告的内容是因为遭到大量 CC 攻击,那为什么这次过去了这么多天不仅服务没有恢复并且连个公告都没有呢?难道说友谊的小船说翻就翻。。。
什么BootCDN 对于没有使用BootCDN的人来说,这可能是个陌生的词汇, BootCDN 是 Bootstrap 中文网支持并维护的前端开源项目免费 CDN 服务,致力于为 Bootstrap、jQuery、Angular、Vuejs 一样优秀的前端开源项目提供稳定、快速的免费 CDN 加速服务。BootCDN 所收录的开源项目主要同步于 cdnjs 仓库。
自2013年10月31日上线以来已经为30多万家网站提供了稳定、可靠的免费 CDN 加速服务。
使用cdn的好处 很多读者就要问了,为什么你网站的css和js资源不放在本地而要依赖cdn呢?这就要说到cdn的好处啦。
1.不用担心自己网站访客,在任何时间,任何地点,任何网络运营商,都能快速打开网站,说白了就是把你的资源交给第三方托管,它快你的网站访问就快,并且不需要消耗你的网站流量,提升访问速度。
2.各种服务器虚拟主机带宽等采购成本,包括后期运维成本都会大大减少。
3.给网站直接带来的好处就是:流量,咨询量,客户量,成单量,都会得到大幅度提升。
想想看,一个打开速度很快的网站和一个打开速度很慢的网站,用户的体验是完全不一样的,那么哪些群体使用更广泛呢?在我看来打开率较高的博客、资讯类的网站群体应当使用较多。
使用cdn的坏处 说完好处也要说说坏处了,对于不使用cdn的网站来说,它崩任他崩,我自坐如松。对于少数资源依赖cdn的网站来说,可能仅影响体验,比如页面显示、js事件等,但对于严重依赖cdn的网站来说,只能说同归于尽了。。。如下图为例
分析此次故障 此次故障是在国庆发生的,一直持续到现在有一周多了,官方最新的公告还显示在去年被攻击的时候,
然后搜索资源的时候,指向的地址都是cdnjs.cloudflare.com这个网站,
在搜索相关资料的时候,小编看到一个近期的一个博主的帖子,让小编也觉得BootCDN已经是弃坑了,大家可以看看相关截图:
看看被坑的网友留言~
解决方案 出了问题快速解决才是王道,这里小编介绍几个方案供大家参考
1.更换较稳定的cdn厂商,小编推荐的是百度、七牛、新浪、又拍云、360、今日头条。但是如果你的网站需要稳定性比较高的,建议不要使用这种方式,要假设任何的第三方都是不可靠的。
2.资源本地化,然后部署的时候自己配缓存等,这也是大部分网站的做法。
总结 个人感觉大部分的免费产品都做不到长久,即使长久点,没有资金维护团队的生存。也无法发展壮大,这就好比做公众号,一旦某天读者看到的是广告,免不了就会有一些吐槽或者取关的人,不过少了这些人,也好。
推荐阅读
关于各种集合的实现看这篇就够了
金九银十,送你一份GitHub上26000+Star的Java面试项目
这是因为当前版本的caffe的cudnn实现与系统所安装的cudnn的版本不一致引起的。
解决办法:
1.将./include/caffe/util/cudnn.hpp 换成最新版的caffe里的cudnn的实现,即相应的cudnn.hpp.
2. 将./include/caffe/layers里的,所有以cudnn开头的文件,例如cudnn_conv_layer.hpp。 都替换成最新版的caffe里的相应的同名文件。
3.将./src/caffe/layer里的,所有以cudnn开头的文件,例如cudnn_lrn_layer.cu,cudnn_pooling_layer.cpp,cudnn_sigmoid_layer.cu。 都替换成最新版的caffe里的相应的同名文件。
转载请标明出处:http://blog.csdn.net/donkor_/article/details/82972790
前言:
最近公司在项目开发中,由于华为手机存在部分的问题。所以购买了部分新款的华为手机。在真机调试项目APP时,老是无法出现Logcat打印出来的各种错误信息。
▲ 问题分析
google查证之后确定,目前国内华为、魅族手机都会有同样的情况。原因是国内的部分厂商定制的手机对于应用中的 Log 日志默认做了打印限制,需要手动设置才能打印出特殊级别的日志。
▲ 解决方案
华为(HUAWEI)
进入工程模式
启动拨号应用,输入:##2846579## 这样一串东西即可
选择“1.后台设置”
选择“3.LOG设置”,全部打开即可
部分魅族(MEIZU)
应用“设置”中进入“开发者选项”
拉到最底下,进入“性能优化”
“高级日志输出”选择“全部允许”即可
About me
Email :donkor@yeah.net
Android开发交流QQ群 : 537891203
该工具类是用于操作Java.lang.String类的。
StringUtils类与String类的区别在于:此类是null安全的,即如果输入参数String为null,则不会抛出NullPointerException异常,代码更健壮。
以函数isEmpty为例子:
存在字符串stringTest, 若该字符串为空,返回
1.使用String类判断方法为:
if(null !=stringTest){ if(stringTest.isEmpty()){ return true; } }else{ return true; } 该方法需要先进行非空判断,已避免空指针。
2.使用StringUtils的判断方法为:
if(StringUtils.isEmpty(stringTest )){ return true; } 查看StringUtils的源码可知:
public static boolean isEmpty(String str) { return (str == null) || (str.length() == 0); } 其相关的操作已经处理好。再查看常见操作trim函数的源码如下:
public static String trim(String str) { return str == null ? null : str.trim(); } 因此可知,使用StringUtils类比原始的String类更加健壮,避免空指针。
常见方法如下:
判断函数:
1.判断是否为空,返回boolean:
StringUtils.isEmpty(String str)
2.判断是否非空,返回boolean:
StringUtils.isNotEmpty(String str)
3.判断空白,返回boolean:
StringUtils.isBlank(String str)
4.判断非空白,返回boolean:
StringUtils.isNotBlank(String str)
JSON简介
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
利用python保存数据到JSON文件
import json str = ''' [{ "name":"zhang san", "gender":"male", "birthday":"1992-01-05" },{ "name":"李四", "gender":"男", "birthday":"1991-12-13" }] ''' data = json.loads(str) with open('json.json','w',encoding='utf-8') as file: file.write(json.dumps(data,indent=2,ensure_ascii=False)) 首先引入json库
定义字符串str
用json.loads()方法将字符串str转换成列表list
with open打开文件,并设置编码格式为utf-8,以保存中文
用json.dumps()方法写入文件,可通过indent控制缩进,ensure_ascii的值设为False以正常显示中文
这半个月断断续续在学习用PHP的ThinkPHP框架开发后端API。现在总结记录一下开发一个接口需要做好哪些事,以此提高开发效率,并且也有不错的扩展性。
一、流程概要 基本是这么一个流程,略过环境搭建:
整理清楚有哪些接口设计数据表 初步梳理是一对一,一对多,还是多对多编写验证器编写全局异常类(AOP思想)定义路由路径建立控制器类建立模型类 用ORM,所以建立和数据表对应的模型类控制器调用模型,模型调用数据库,完成接口编写 二、具体说明 梳理好有哪些接口后,就开始设计数据表:
数据表会随着代码的编写做些调整和改变。
值得注意的一点,当有两张表之间的关系是多对多时,记得设计一张中间表存放两张表各自的id。
设计好数据表后,开始编写一些工具类,有助于提高编写业务代码时的效率。
首先是验证器(validate)。
TP5框架自带验证器类,我们要做的则是继承这个验证器类,然后根据具体的接口做扩展即可。
创建一个验证器基类,把通用的方法放在里面:
goCheck()方法是所有具体验证器都会调用的方法,各个具体验证器只是会重写一些验证规则和验证返回信息而已。
在goCheck()方法里,实例化了Request类。这样做的目的是获取API被调用时,调用方传递的参数。获取到参数后,自然就是对这些参数进行验证了。check()方法会调用各个具体验证器里设置的验证规则函数进行检测。
然后是全局异常类(global exception)。
同样的,TP5框架自带了一个异常类,我们就创建一个异常基类继承它。
随后需要做的则是根据具体的接口重写HTTP状态码,错误消息和错误码即可。
至于错误码的定义,则是自己设计一套规范。
搭建好验证器和全局异常类后,我们只需要在每个接口的函数里面调用他们就行了:
好,至此一些基础的东西就搭建好了,下面开始编写接口代码。
首先定义路由路径:
在route.php里,引入Route类,定义路径即可。路径里的变量用:号+变量名表示,路径里的变量由路径末尾指定的函数接收,这个函数定义在控制器相对应的类里面。
比如id这个变量:
如上图,在控制器里,当拿到调用方通过路由路径传过来的参数后,我们就调用模型,把参数传过去,模型处理具体的数据库调用。
这里也是一个需要注意的点,控制器尽量只做连接的事情,不做具体的操作。
然后,在建立了控制器后,顺理成章,也需要建立对应的模型。
TP5同样自带了Model类,然后我们也定义自己的模型基类,当然也是继承TP5的模型类:
模型基类自然也是定义较为通用的方法。比如上图的例子里,定义了一个返回图片前缀链接的方法,不同的接口但又跟图片调用有关的话,就会用到这个方法来拼接图片URL。
这里也有个注意的点。当我们需要创建全局的变量时,可以在application目录下创建extra目录文件,然后创建setting.php文件,在里面返回一个关联数组即可:
随后的调用如上图模型基类里的prefixImgUrl方法里展示的一样,config函数,参数传入文件名加关联数组的key值,这样就可以获取到了:
回到模型上来,每个接口会有自己的模型类,这个模型类对应一张数据表,比如:
Banner模型类由于是通过模型基类继承了TP5的Model类,我们需要做的就是重写一些属性,来适应这个具体的接口,比如重写$hidden属性,定义这个接口返回的哪些字段我们是要隐藏的。
然后则是ORM的重点之一,调用数据表所对应的模型类。比如items方法里,通过hasMany()这个方法确定了Banner模型和BannerItem模型的关系。然后在getBannerById()方法里,调用了ORM用来操作数据的方法,这是对原生操作数据库语句的封装,然后ORM会返回模型对象,这个对象除了带有数据库数据外,还会带有一些属性和方法,用来操作数据。这是ORM对比原生SQL语句的一个优势。
最后,控制器调用模型的getBannerById()方法,获取到了数据,再作为接口的返回值传递给接口调用者。这样就完成了一次接口的编写。
三、总结 至此做了一个简要的后端API开发流程记录。其中还有很多细节没有提到,只是简略的描述了一个过程,不过这也不是这次记录的主要目的。这次的目的还是对这一周多学习的一个记录。
通过这次学习后端API开发,更加巩固了我对面向对象编程里思想的理解和运用。
通过继承和重写,可以把代码写得更干净简洁。
类,实例,属性,方法,怎么看待他们,然后操作他们,通过这次学习又加深了很多认识。
多级缓存 基本概念如何缓存数据分布式缓存与应用负载均衡热点数据与更新缓存缓存崩溃与快速修复 基本概念 1. 什么是多级缓存 是指在整个系统架构的不同系统层级进行数据缓存、以提高访问效率 一般会使用nginx本地缓存解决热点缓存问题 使用分布式缓存减少访问回源率 使用tomcat堆缓存用于防止缓存失效/崩溃之后的冲击 如何缓存数据 1. 过期与不过期 1) 不过期缓存场景 业务 -> 开启事务 -> 执行SQL -> 提交事务 -> 写缓存 首先会写db、若成功则写缓存、会存在db写成功、但缓存写失败、但无法回滚事务的情况 还应该避免把写缓存放在事务中、尤其是写分布式缓存、因为网络抖动可能导致写缓存响应时间很慢、引起事务阻塞、若对缓存数据的一致性要求没那么高、数据量没那么大、可以考虑定期全量同步缓存 2) 过期缓存、eg. 采用懒加载、一般用于缓存其它系统的数据(如无法订阅变更消息或者成本很高)、缓存空间有限、低频热点缓存等场景 常见步骤是:读cache、无 -> 查询并缓存 -> 下次读cache ok、这种情况缓存可能存在一段时间的数据不一致、需要根据场景来决定如何设计过期时间、如库存数据可以在前端应用上缓存几秒、短时间内的不一致是可以忍受的 2. 维度化缓存 对于电商系统、一个商品可能拆分成如基础属性、图片列表、上下架、规格参数、商品介绍等 若商品变更、需要把所有的数据都更新一遍、更新成本很高、包括接口调用量和带宽、因此最好将数据进行维度化并增量更新(只变更变动部分)、尤其是上下架状态、这种只是一个状态但变更频繁的数据、维度化后可以减少server很多压力 3. 大value缓存 遇到需要大value缓存的情况、可以考虑多线程实现的缓存(eg. memcache)或者对value进行压缩、或者将value拆分成多个小value、客户端再进行查询、聚合 4. 热点缓存 对于热点缓存、若每次都从远程缓存获取、可能会因为访问量太大而导致远程缓存系统请求过多、负载过高或者带宽过高等问题、最终可能导致缓存响应慢、使客户端请求超时 1) 通过挂载更多的从缓存、客户端通过负载均衡机制读取从缓存系统数据、 2) 也可以在客户端所在的应用/代理层本地存储一份、避免访问远程缓存、即eg.库存量、在有些应用系统中也可以进行几秒钟的本地缓存、降低远程系统的压力 分布式缓存与应用负载均衡 1. 缓存分布式 一般采用分片实现、即将数据分散到多个实例或者多台服务器 算法一般采用取模和一致性hash 如果是不过期缓存、可以考虑取模机制、扩容时新建一个集群、对于可以丢失的缓存数据、可以考虑一致性hash、即使一个实例出问题、也只是丢一小部分、对于分片实现可以考虑客户端实现、或者使用如 twemproxy的中间件进行代理(分片对客户端是透明的)、如果使用redis、则可以考虑redis-cluster分布式集群方案 2. 应用负载均衡 一般采用轮询和一致性hash、 一致性hash可以根据应用请求的url或者url参数将相同的请求转发到同一个节点 轮询是将请求均匀的转发到每个服务器 轮询的优点是:请求更加均匀、使得每个服务器的负载基本均衡、 缺点是:随着nginx应用server的增加、缓存的命中率会下降、 eg.原来10台server的命中率为90%、再增加10台、可能下降到45% 这种方式不会因为热点问题导致其中一台server的负载过重 一致性hash的优点是:相同的请求都会转发到同一台server、命中率不会因为server的增加而降低 缺点是:因为相同的请求会转发到同一server、可能会造成某台机器负载过重 可以根据实际情况选择使用哪种算法 1. 负载较低时、使用一致性hash 2.
应用级别的缓存
基本概念回收策略java缓存类型多级缓存 基本概念 缓存命中率:从缓存查到的次数 / 查询总次数(缓存查询次数+快慢设备中读取的次数) SOR:system of record记录系统、或者可以叫数据源、即实际存储原始数据的系统 Cache:缓存、是SOR的快照数据、cache的访问速度比SOR要快、放入cache的目的是提升访问速度、减少回源到SOR的频次 回源:cache未命中时、需要从SOR读取数据、这个过程叫回源 回收策略 1. 基于空间:指缓存设置了存储空间、eg.设置10MB、当达到存储空间上限时、按照一定的策略移除数据 2. 基于时间: 1) TTL(Time to Live): 存活期、即缓存数据从创建开始直到到期的一个时间段(不管在这个时间段内有没有被访问、缓存数据都将过期) 2) TTI(Time To Idle): 空闲期、即缓存数据多久没被访问后会被删除 3. 基于容量:指缓存设置了最大大小、当缓存的条目超过最大大小时、按照一定的策略移除旧数据 4. 基于java对象引用 1) 软引用:若一个对象是软引用、那么当JVM堆内存不足时、垃圾回收器可以回收这些对象、 比较适合做缓存、这样当jvm堆内存不足时、可以回收这些对象、供强引用对象使用、从而避免OOM 2) 弱引用:当垃圾回收器回收内存时、若发现弱引用、会立即回收、相对软引用、弱引用的生命周期会更短 注意:只有当没有其它对象引用该弱引用/软引用对象时、垃圾回收才会回收改引用 5. 回收算法:使用基于空间和基于容量的缓存会使用一定的策略移除旧数据、常用的如下: 1) FIFO(First in first out): 先进先出 2) LRU(Least Recently Used): 最近最少使用算法、使用时间距离现在最久的那个会被移除 3) LFU(Least Frequency Used): 最不常用算法、一定时间段内使用次数(频率)最小的被移除 java缓存类型 堆缓存:使用java堆缓存来存储缓存对象、无需序列化/反序列化、是最快的缓存、缺点也很明显、当缓存的数据量很大时、GC暂停的时间会变长、存储容量受限于堆空间大小、一般通过软引用/弱引用来存储缓存对象、即当堆内存不足时、可以强制回收这部分内存释放堆内存空间、一般使用堆缓存存储较热的数据、可以使用Guava Cache、Ehacache 3.x、MapDB来实现 堆外缓存:即使用缓存数据存储在堆外缓存、可以减少GC暂停时间(堆对象转移到堆外、GC扫描和移动的对象变少了)、可以支持更大的缓存空间(只受机器大小限制、不受堆空间的影响)、但是读取数据时要序列化和反序列化、会比堆缓存慢 磁盘缓存:即缓存数据存储在磁盘上、在jvm重新启动时、缓存数据依然存在、而堆缓存/堆外缓存的数据会丢失、需要重新加载、可以使用Ehacache 3.x、mapdb来实现 分布式缓存:上边提到的缓存是进程内缓存和磁盘缓存、在多jvm实例的情况下、会存在2个问题: 1. 单机容量问题 2. 数据一致性问题(多台据jvm实例的缓存数据不一致怎么办?)--短时间内数据不一致可以接受 3. 缓存命不中时、需要多次回源请求(每个实例都会回源一次)、可以使用一致性hash分片算法解决 可以使用分布式缓存来解决、eg. 使用ehcache-clustered(配个terracotta server)来实现进程间分布式缓存、也可以使用如redis等实现分布式缓存 多级缓存 可以使用本地缓存 + 分布式缓存来提升缓存性能