首页 文章 精选 留言 我的

精选列表

搜索[然之],共10000篇文章
优秀的个人博客,低调大师

Pytorch之Tensor大详解

导语 深度学习框架Pytorch发展势头惊人,这点小编也深有体会,翻翻Github上深度学习的开源代码,发现用Pytorch真的多了不少,所以小编最近也正在入坑Pytorch,顺便写写文章做些总结。认真看完这篇文章,你将收获: 理解Tensor的创建 理解Tensor的加速 理解Tensor的常用属性 理解Tensor的常用方法 Tensor创建 我们应该都知道Numpy是支持大量维度数组与矩阵运算的常用扩展库。但是对于计算图,深度学习或者梯度,Numpy似乎真的有心无力,因为它的计算无法像Tensor一样在GPU上加速。今天我们就一起来谈谈Pytorch最基本的概念Tensor。 Tensor是n维的数组,在概念上与numpy数组是一样的,不同的是Tensor可以跟踪计算图和计算梯度。 1.从Numpy中创建 import torch import numpy as np numpy_array= np.array([1,2,3]) torch_tensor1 = torch.from_numpy(numpy_array) torch_tensor2 = torch.Tensor(numpy_array) torch_tensor3 = torch.tensor(numpy_array) 值得注意的是torch.Tensor()是默认张量类型torch.FloatTensor()的别名,就是说返回torch.Tensor()返回的是Float数据类型。其实我们也可以修改其默认的数据类型: torch.set_default_tensor_type(torch.DoubleTensor) 而torch.tensor()则会根据输入数据类型生成相应的torch.LongTensor、torch.FloatTensor和torch.DoubleTensor。 当然,我们也可以通过numpy()把Tensor转换为numpy类型 numpy_array = torch_tensor1.numpy() # 如果tensor在CPU上 numpy_array = torch_tensor1.cpu.numpy() # 如果tensor在GPU上 print(type(numpy_array)) #输出 : <class 'numpy.ndarray'> 注意,如果Tensor在GPU上,则需要使用.cpu()先将GPU的Tensor转换到cpu上。 2.从Python内置类型中创建 lst = [1,2,3] torch_tensor1 = torch.tensor(a) tp = (1,2,3) torch_tensor2 = torch.tensor(a1) 3.其他方式 # 创建相同元素的Tensor torch_tensor1 = torch.full([2,3],2) # 创建全为1的Tensor torch_tensor2 = torch.ones([2,3]) # 创建全为0的Tensor torch_tensor3 = torch.zeors([2,3]) # 创建对角阵的Tensor torch_tensor4 = torch.eye(3) # 在区间[1,10]中随机创建Tensor torch_tensor5 = torch.randint(1,10,[2,2]) # 等等... 创建Tensor时候也可指定数据类型和所存储的设备 torch_tensor= torch.zeros([2,3],dtype=torch.float64,device=torch.device('cuda:0')) torch_tensor.dtype #torch.float64 torch_tensor.device #cuda:0 torch_tensor.is_cuda #True Tensor加速 我们可以使用以下两种方式使得Tensor在GPU上加速。 第一种方式是定义cuda数据类型。 dtype = torch.cuda.FloatTensor gpu_tensor = torch.randn(1,2).type(dtype) #把Tensor转换为cuda数据类型 第二种方式是直接将Tensor放到GPU上(推荐)。 gpu_tensor = torch.randn(1,2).cuda(0)#把Tensor直接放在第一个GPU上 gpu_tensor = torch.randn(1,2).cuda(1)#把Tensor直接放在第二个GPU上 而将Tensor放在CPU上也很简单。 cpu_tensor = gpu_tensor.cpu() Tensor常用属性 1.查看Tensor类型属性 tensor1 = torch.ones([2,3]) tensor1.dtype # torch.float32 2.查看Tensor尺寸属性 tensor1.shape # 尺寸 tenosr1.ndim #维度 3.查看Tensor是否存储在GPU上 tensor1.is_cuda #False 4.查看Tensor存储设备 tensor1.device # cpu tensor1.cuda(0) tensor1.device # cuda:0 5.查看Tensor梯度计算 tensor1.grad Tensor常用方法 1.torch.squeeze():删除值为1的维度并返回Tensor tensor1 = torch.ones([2,1,3]) torch_tensor1.size() #torch.Size([2, 1, 3]) tensor2=torch.squeeze(tensor1) print(tensor2.size())#torch.Size([2, 3]) 从例子中可以看到Tensor的维度从原来的[2,1,3]变成[2,3],值为1的维度被删除。 2.torch.Tensor.permute()置换Tensor维度并返回新视图。 tensor1 = torch.ones([2,1,3]) print(tensor1.size()) # torch.Size([2, 1, 3]) tensor2 = tensor1.permute(2,1,0) # 0,1,2-> 2,1,0 print(tensor2.size()) # torch.Size([3, 1, 2]) 从例子中可以看到,Tensor将原本的第一个维度值为2,第2维度值为3,置换后分别变成3和2. 3.torch.Tensor.expand():把值为1的维度进行扩展,扩展的Tensor不会分配新的内存,只是原来的基础上创建新的视图并返回。 >>>tensor1 = torch.tensor([[3],[2]]) >>>tensor2 = tensor1.expand(2,2) >>>tensor1.size() torch.Size([2, 1]) >>>tensor2 tensor([[3, 3], [2, 2]]) >>>tensor2.size() torch.Size([2, 2]) 从例子中可以看到,原本Tensor的维度是(2,1),因为该函数是对值为1的维度进行扩展,所以可以扩展为(2,2),(2,3)等,但是要注意的是保持非1值的维度不变。 4.torch.Tensor.repeat():沿着某个维度的Tensor进行重复,和expand()不同的是,该函数复制原本的数据。 >>>tensor1 = torch.tensor([[3],[2]]) >>>tensor1.size() torch.Size([2, 1]) >>>tensor2=tensor1.repeat(4,2) >>>tensor2.size() torch.Size([8, 2]) >>>tensor2 tensor([[3, 3], [2, 2], [3, 3], [2, 2], [3, 3], [2, 2], [3, 3], [2, 2]]) 例子中的tensor1维度是(2,1),tensor1.repeat(4,2),这是对第0维度和第1维度的Tensor分别重复4次和2次,所以repeat后维度变成了(8,2)。再看下面的例子。 >>>tensor1 = torch.tensor([[2,1]]) >>>tensor1.size() torch.Size([1, 2]) >>>tensor2=tensor1.repeat(2,2,1) >>>tensor2.size() torch.Size([2,2,2]) >>>tensor2 tensor([[[2, 1], [2, 1]], [[2, 1], [2, 1]]]) 例子中的tensor1维度是(1,2),tensor1.repeat(2,2,1),此时前者跟后者的维度对应不上,此时可以理解为tensor1把维度改写为(1,1,2),再tensor1.repeat(2,2,1),tensor1第0,1,2的维度的Tensor分别重复1次,1次,2次,repeat后Tensor维度为(2,2,2)。 公众号:CVpython,专注于分享Python和计算机视觉,我们坚持原创,不定期更新,希望文章对你有帮助,快点扫码关注吧。

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

Java基础之Java语法

Java语法基础部分汇总 1,关键字:其实就是某种语言赋予了特殊含义的单词。 保留字:其实就是还没有赋予特殊含义,但是准备日后要使用过的单词。 2,标示符:其实就是在程序中自定义的名词。比如类名,变量名,函数名。包含0-9、a-z、$、_; 注意: 1),数字不可以开头。 2),不可以使用关键字。 3,常量:是在程序中的不会变化的数据。 4,变量:其实就是内存中的一个存储空间,用于存储常量数据。 作用:方便于运算。因为有些数据不确定。所以确定该数据的名词和存储空间。 特点:变量空间可以重复使用。 什么时候定义变量?只要是数据不确定的时候,就定义变量。 变量空间的开辟需要什么要素呢? 1,这个空间要存储什么数据?数据类型。 2,这个空间叫什么名字啊?变量名称。 3,这个空间的第一次的数据是什么?变量的初始化值。 变量的作用域和生存期: 变量的作用域: 作用域从变量定义的位置开始,到该变量所在的那对大括号结束; 生命周期: 变量从定义的位置开始就在内存中活了; 变量到达它所在的作用域的时候就在内存中消失了; 数据类型: 1):基本数据类型:byte、short、int、long、float、double、char、boolean 2):引用数据类型:数组、类、接口。 级别从低到高为:byte,char,short(这三个平级)-->int-->float-->long-->double 自动类型转换:从低级别到高级别,系统自动转的; 强制类型转换:什么情况下使用?把一个高级别的数赋给一个别该数的级别低的变量; 运算符号: 1)、算术运算符。 +-*/%%:任何整数模2不是0就是1,所以只要改变被模数就可以实现开关运算。 +:连接符。 ++,-- 2)、赋值运算符。 =+=-=*=/=%= 3)、比较运算符。 特点:该运算符的特点是:运算完的结果,要么是true,要么是false。 4)、逻辑运算符。 &|^!&&|| 逻辑运算符除了!外都是用于连接两个boolean类型表达式。 &:只有两边都为true结果是true。否则就是false。 |:只要两边都为false结果是false,否则就是true ^:异或:和或有点不一样。 两边结果一样,就为false。 两边结果不一样,就为true. &和&&区别:&:无论左边结果是什么,右边都参与运算。 &&:短路与,如果左边为false,那么右边不参数与运算。 |和||区别:|:两边都运算。 ||:短路或,如果左边为true,那么右边不参与运算。 5)、位运算符:用于操作二进制位的运算符。 &|^ <<>>>>>(无符号右移) 练习:对两个变量的数据进行互换。不需要第三方变量。 inta=3,b=5;-->b=3,a=5; a=a+b;a=8; b=a-b;b=3;c a=a-b;a=5; a=a^b;// b=a^b;//b=a^b^b=a a=a^b;//a=a^b^a=b; 练习:高效的算出2*8------------------>位移运算的考验,java基础面试中它的曝光率不低哦 5,语句 Ifswitchdowhilewhilefor 这些语句什么时候用? 1)、当判断固定个数的值的时候,可以使用if,也可以使用switch。 但是建议使用switch,效率相对较高。 switch(变量){ case值:要执行的语句;break; … default:要执行的语句; } 工作原理:用小括号中的变量的值依次和case后面的值进行对比,和哪个case后面的值相同了 就执行哪个case后面的语句,如果没有相同的则执行default后面的语句; 细节:1):break是可以省略的,如果省略了就一直执行到遇到break为止; 2):switch后面的小括号中的变量应该是byte,char,short,int四种类型中的一种; 3):default可以写在switch结构中的任意位置;如果将default语句放在了第一行,则不管expression与case中的value是否匹配,程序会从default开始执行直到第一个break出现。 2)、当判断数据范围,获取判断运算结果boolean类型时,需要使用if。 3)、当某些语句需要执行很多次时,就用循环结构。 while和for可以进行互换。 区别在于:如果需要定义变量控制循环次数。建议使用for。因为for循环完毕,变量在内存中释放。 break:作用于switch,和循环语句,用于跳出,或者称为结束。 break语句单独存在时,下面不要定义其他语句,因为执行不到,编译会失败。当循环嵌套时,break只跳出当前所在循环。要跳出嵌套中的外部循环,只要给循环起名字即可,这个名字称之为标号。 代码片段: z://for循环标号 for(intx=0;x<3;x++){ for(inty=0;y<2;y++){ //不带标号的就是结束整个循环体的作用,在那个循环内部就结束哪个循环 if(x==1)break; //带标号跳过break后面的语句,回到标号位置的循环,继续该循环下次的条件判断, //已决定是否执行该循环体 if(x==2&&y==1)breakz; } } continue:只作用于循环结构,继续循环用的。 作用:结束本次循环,继续下次循环。该语句单独存在时,下面不可以定义语句,执行不到。 6,函数: 为了提高代码的复用性,可以将其定义成一个单独的功能,该功能的体现就是java中的函数。函数就是体现之一。 java中的函数的定义格式: 修饰符返回值类型函数名(参数类型形式参数1,参数类型形式参数1,…){ 执行语句; return返回值; } 当函数没有具体的返回值时,返回的返回值类型用void关键字表示。 如果函数的返回值类型是void时,return语句可以省略不写的,系统会帮你自动加上。 return的作用:结束函数。结束功能。 如何定义一个函数? 函数其实就是一个功能,定义函数就是实现功能,通过两个明确来完成: 1)、明确该功能的运算完的结果,其实是在明确这个函数的返回值类型。 2)、在实现该功能的过程中是否有未知内容参与了运算,其实就是在明确这个函数的参数列表(参数类型&参数个数)。 函数的作用: 1)、用于定义功能。 2)、用于封装代码提高代码的复用性。 注意:函数中只能调用函数,不能定义函数。 主函数: 1)、保证该类的独立运行。 2)、因为它是程序的入口。 3)、因为它在被jvm调用。 函数定义名称是为什么呢? 答:1)、为了对该功能进行标示,方便于调用。 2)、为了通过名称就可以明确函数的功能,为了增加代码的阅读性。 重载的定义是:在一个类中,如果出现了两个或者两个以上的同名函数,只要它们的参数的个数,或者参数的类型不同,即可称之为该函数重载了。 如何区分重载:当函数同名时,只看参数列表。和返回值类型没关系。 7,数组: 用于存储同一类型数据的一个容器。好处:可以对该容器中的数据进行编号,从0开始。数组用于封装数据,就是一个具体的实体。 如何在java中表现一个数组呢?两种表现形式。 1)、元素类型[]变量名=new元素类型[元素的个数]; 2)、元素类型[]变量名={元素1,元素2...}; 元素类型[]变量名=new元素类型[]{元素1,元素2...}; --------------------------------------------------------- //二分查找法。必须有前提:数组中的元素要有序。 publicstaticinthalfSeach_2(int[]arr,intkey){ intmin,max,mid;//定义最小,最大,中间数 min=0;//最小为0 max=arr.length-1;//最大为数组的长度-1 mid=(max+min)>>1;//(max+min)/2;//中间数为最大加最小除以2 while(arr[mid]!=key){//如果数组中間值不等于key if(key>arr[mid]){//如果key>中間值 min=mid+1; } elseif(key max=mid-1; if(max return-1; mid=(max+min)>>1; } returnmid; } 知识拓展: java内存。 1:寄存器。2:本地方法区。3:方法区。4:栈。5:堆。 栈:存储的都是局部变量(函数中定义的变量,函数上的参数,语句中的变量); 只要数据运算完成所在的区域结束,该数据就会被释放。 堆:用于存储数组和对象,也就是实体。啥是实体呢?就是用于封装多个数据的。 1:每一个实体都有内存首地址值。 2:堆内存中的变量都有默认初始化值。因为数据类型不同,值也不一样。 3:垃圾回收机制。

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

SpringCloud之zuul源码解析

Zuul源码解析 zuul各版本实现存在一些微小的变化,总的实现思想未改变,以spring-cloud-netflix-core-1.3.6.RELEASE为例 一、zuul的重要的初始化类 org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration org.springframework.cloud.netflix.zuul.ZuulFilterInitializer org.springframework.cloud.netflix.zuul.RibbonCommandFactoryConfiguration ZuulServerAutoConfiguration 初始化路由规则 初始化一些重要的filter如PreDecorationFilter,RibbonRoutingFilter 初始化ZuulFilterInitializer 初始化ZuulHandlerMapping 代码如下 //路由规则 @Bean @ConditionalOnMissingBean(DiscoveryClientRouteLocator.class) public DiscoveryClientRouteLocator discoveryRouteLocator() { return new DiscoveryClientRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties, this.serviceRouteMapper); } // pre filters @Bean public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) { return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties, proxyRequestHelper); } // route filters @Bean public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory<?> ribbonCommandFactory) { RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers); return filter; } @Configuration protected static class ZuulFilterConfiguration { @Autowired private Map<String, ZuulFilter> filters; @Bean public ZuulFilterInitializer zuulFilterInitializer( CounterFactory counterFactory, TracerFactory tracerFactory) { FilterLoader filterLoader = FilterLoader.getInstance(); FilterRegistry filterRegistry = FilterRegistry.instance(); return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry); } } @Bean public ZuulController zuulController() { return new ZuulController(); } @Bean public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) { ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController()); mapping.setErrorController(this.errorController); return mapping; } ZuulProxyAutoConfiguration zuulProxAutoConfiguration继承ZuulServerAutoConfiguration功能上和zuulServerAutoConfiguration 主要功能是增加了RibbonCommandFactoryConfiguration的配置,初始化所有的实现ribbon的方式如apache,okhttp。 ZuulFilterInitializer 该类的作用主要是把初始化的过滤器注册到zuul的FilterRegistry,FilterRegistry是一个单例用于初始化路由信息,在ZuulRunner中使用 RibbonCommandFactoryConfiguration 主要作用是配置转发的实现,实现主要有apache,okhttp 二、zuul的转发实现 首先第一步转到ZuulHandlerMapping中的lookupHandler方法,把转发转到zuulController中 @Override protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) { return null; } String[] ignored = this.routeLocator.getIgnoredPaths().toArray(new String[0]); if (PatternMatchUtils.simpleMatch(ignored, urlPath)) { return null; } RequestContext ctx = RequestContext.getCurrentContext(); if (ctx.containsKey("forward.to")) { return null; } if (this.dirty) { synchronized (this) { if (this.dirty) { registerHandlers(); this.dirty = false; } } } return super.lookupHandler(urlPath, request); } 第一次访问时dirty为true会初始化一次请求规则如下 private void registerHandlers() { Collection<Route> routes = this.routeLocator.getRoutes(); if (routes.isEmpty()) { this.logger.warn("No routes found from RouteLocator"); } else { for (Route route : routes) { registerHandler(route.getFullPath(), this.zuul); } } } 第二步ZuulController继承ServletWrappingController的会把请求转到ZuulServlet中如下 /** * @author Spencer Gibb */ public class ZuulController extends ServletWrappingController { public ZuulController() { setServletClass(ZuulServlet.class); setServletName("zuul"); setSupportedMethods((String[]) null); // Allow all } @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { try { // We don't care about the other features of the base class, just want to // handle the request return super.handleRequestInternal(request, response); } finally { // @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter RequestContext.getCurrentContext().unset(); } } } 第三步ZuulServlet的service方法如下主要执行pre,route,postRoute三种路由器 @Override public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException { try { init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse); // Marks this request as having passed through the "Zuul engine", as opposed to servlets // explicitly bound in web.xml, for which requests will not have the same data attached RequestContext context = RequestContext.getCurrentContext(); context.setZuulEngineRan(); try { preRoute(); } catch (ZuulException e) { error(e); postRoute(); return; } try { route(); } catch (ZuulException e) { error(e); postRoute(); return; } try { postRoute(); } catch (ZuulException e) { error(e); return; } } catch (Throwable e) { error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName())); } finally { RequestContext.getCurrentContext().unset(); } } 四、最后由SendResponseFilter执行返回结果,filterOrder为1000所以最好post的filter不要超过1000否则影响返回结果

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

ECS之弹性供应组

您是否遇见过创建ECS实例却生产不出来的问题?您是否苦于多次操作才能创建1000台实例的烦恼?你是否期待用更加优惠的价格来使用ECS资源?今天阿里云为您提供了一种交付ECS实例资源的全新方式:弹性供应组,可以快速的解决您遇到的相关问题。 概念 弹性供应组(Auto Provisioning Group,APG)是一个使用抢占式实例和按量付费实例快速部署实例集群的方案,支持一键部署跨计费方式、跨可用区、跨实例规格族的实例集群。弹性供应以供应组为载体调度和维护计算资源,您可以通过弹性供应组稳定提供计算力,缓解抢占式实例的回收机制带来的不稳定因素,免去重复手动创建实例的繁琐操作。 异同点 弹性供应组相比于已有的RunInstances最基础交付ECS资源的方式,具有更大的灵活性。弹性供应组可以充分发挥跨计费方式、跨可用区、跨实例规格族的能力,避

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

web前端javaScript 之 【Ajax】

Ajax是干什么的,为什么要学这个技术: 我们在上网浏览网站的时候,经常会看到下拉加载的的功能。例如百度图片、淘宝列表等等……,这种我们每次下滑,就自动加载更多的信息。这种信息不用每次刷新页面,而就会自动加载出来;那么我们前端,像这种不必刷新页面也能获取新的数据的技术,我们就需要使用ajax来实现。而ajax也是目前Web开发人员必须要掌握一种技术。简单来讲,ajax就是提交并且接收了服务器的数据,从而可以获取新的数据,来渲染页面的更新 Ajax技术核心是XMLHttpRequest对象(简称XHR),帮助记忆,可以想象是“小黄人”的拼音简称。这是由微软首先引入的一个特性,其他浏览器提供商也都是后来慢慢相同的实现这个技术。也是在2005年低,2006年初,Ajax技术才慢慢的红遍了技术圈。IE5是第一款引入的XHR对象的浏览器,目前我们只讲解适合于IE7之后的版本,之前的不做讲解。 ---用法:--- 一、创建一个ajax对象 首先我们需要构造出一个新的XMLHttpRequest var xhr = new XMLHttpRequest() 二、配置这个对象open(发送方式, 地址, 是否异步); 构造完之后,我们需要创建一个请求发送我们的数据,因为向服务器发送了请求,我们才能得到想要的数据。创建我们需要一个固定的方法open()。 open()它有三个参数,第一个是请求的方式(五种方式 :get/post/put/delete/head),第二个是请求的地址URL,第三个是个布尔值(true/false)代表是否异步。 比如我们向一个后端接口/villin.php发一个get请求,大部分的请求都是异步加载,我们我们就可以这样写。 var xhr = new XMLHttpRequest(); xhr.open('get', './villin.php', true); 三、发送请求 这就创造出了一个简单的请求数据,那么接下来我们要把这个数据发送给数据库,方法send(null); var xhr = new XMLHttpRequest(); xhr.open('get', './villin.php', true); xhr.send(null) send()方法按照中文意思理解,就是发送,它接收一个参数,作为请求主体发送的数据。如果不需要通过请求主体发送数据,就传入一个null,也可以不传,但是对于某些浏览器来说,是必须要传的,所以我们规范一点,写上null。 四、监听响应 这时,后端服务器接收到了我们发送的数据,就会返回我们想要的数据。 XHR的技术价值就是可以实时监听数据的变化,实时返回新的数据,那么怎么监听?他自身携带了一个属性,叫onreadystatechange,从字面意思理解,就是实时监听改变。所以我们监听之后要做的事件,都需要写在onreadystatechange函数当中。 onreadystatechange会监听一个叫readyState的值,这个值很重要,它会反映出当前响应过程的活动阶段,用0~4来表示: 0表示XHR刚创建出来,还没有初始化,没调用open()。1表示已经调用open()方法,但还没有调用send()。2表示已经调用send()方法,但还没有接收到数据。3表示服务器已经接收到了部分响应数据。4表示服务器已经接收到了全部响应数据,可以在客户端使用了。 所以当readyState返回4的时候,说明数据返回成功,我们就可以拿到相应的数据了。 var xhr = new XMLHttpRequest(); xhr.open('get', './villin.php', true); xhr.onreadystatechange = function () { if (this.readyState !== 4) { return; }else{ //成功返回数据,在这里获取数据 } } xhr.send(null) // 要成功发送请求头信息,onreadystatechange一定要在open()之后,send()之前监听。 此时说明服务器已经接收到了全部响应(但响应未必是返回成功的数据)。那么服务器会把响应数据返回给浏览器,浏览器也就返回相应的状态码status,表示数据异常或成功。常见的状态码如下: 2开头,代表响应成功,如200。3开头,代表重定向,如302页面暂时重定向,304表示请求的资源并没有被修改,可以使用浏览器中缓存的版本。4开头,请求有错误,如400。5开头,服务器报错,如500。 所以我们要使用返回来的数据,先判断是否返回正常,一般是>=200并且<300。返回成功后,数据会被放置在一个叫responseText当中,我们直接打印出responseText就可以提取出内部的文本。 var xhr = new XMLHttpRequest(); xhr.open('get', './villin.php', true); xhr.onreadystatechange = function () { if (this.readyState !== 4) { return; }else{ //成功返回数据,在这里获取数据 if(this.status >= 200 && this.status < 300) { console.log('请求成功') console.log(this.responseText) // 此刻拿到后端的数据,打印出responseText }else{ console.log('请求出错') } } } xhr.send(null) // 要成功发送请求头信息,onreadystatechange一定要在open()之后,send()之前监听。 ---超时设定:timeout--- 最初,IE8为XHR提供了超时属性timeout,表示请求在等待多少毫秒之后就终止。再给timeout设置超时设定后,在规定时间没有接收到请求,那么就会触发timeout,进而调用ontimeout程序,如下: var xhr = new XMLHttpRequest(); xhr.open('get', './villin.php', true); xhr.onreadystatechange = function () { if (this.readyState !== 4) { return; }else{ try{ //成功返回数据,在这里获取数据 if(this.status >= 200 && this.status < 300) { console.log('请求成功') console.log(this.responseText) // 此刻拿到后端的数据,打印出responseText }else{ console.log('请求出错') } }catch(val){ // 可以写timeout处理程序 } } } xhr.timeout = 2000; // 设置超时时间为2000mm,超出会调用ontimeout事件 xhr.ontimeout = function(){ alert('请求超时2000mm') } xhr.send(null) // 要成功发送请求头信息,onreadystatechange一定要在open()之后,send()之前监听。 ️注意:如果在超时之后再访问status就会出现错误,所以我们可以将判断封装到try...catch语句,这样就可以完美的解决超时问题 ️虽然timeout是由IE提供的,不过目前其他浏览器的兼容性,还需要大家去尝试。 ---load事件--- Firefox在实现Ajax的简化模型时,引入了load事件。它可以代替onreadystatechange事件,响应完毕后将触发onload事件,所以我们就可以省去了检查readyState属性了,不比判断readyState是否等于4的问题了。 var xhr = new XMLHttpRequest(); xhr.open('get', './villin.php', true); xhr.onload = function () { if(this.status >= 200 && this.status < 300) { console.log('请求成功') console.log(this.responseText) // 此刻拿到后端的数据,打印出responseText }else{ console.log('请求出错') } } xhr.send(null) 目前Firefox、Oprea、Chrome、Safari都支持load事件。 ---浏览器跨域--- 通过XHR实现ajax通信的主要一个主要限制,就是来自跨域的安全策略(同源策略)。默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源,这种安全策略是为了预防某些恶意行为。 怎么解决跨域问题,其实IE与其他浏览器还各有千秋,比如IE与其他浏览器的CORS的实现,在open()方法中传入对决URL地址,如www开头的地址,我们就可以拿到绝对地址上的信息。还有一起其他跨域技术: JSONP Jsonp虽然和json长得很像,不过这两个还是没什么关系的,json是以键和值的方式存在的一种格式,Jsonp是由回调函数和数据组成的一种技术。 Jsonp是通过动态<script>元素来使用的,使用的时候可以为src指定一个跨域的URL。<script>元素有能力不受限制的从其他域中加载资源,所以可以实现跨域请求。 <script src='www.avillin.com/?callback=handleaaa'></script> // URL就是此地址,后边callback返回的就是函数,名称为handleaaa <script> function handleaaa(response){ console.log(response) } </script> 扩展:目前前端使用的vue框架中,也有自己的跨域方式,可以通过proxy来实现跨域。 更多关于ajax的知识,也欢迎一起探讨。 本文作者:avillin 邮箱:avillin@163.comv-chat:villinWechat

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

Docker之11 Docker Swarm

什么是Docker Swarm Docker Swarm是官方对容器的编排项目之一,可以在多个主机上创建集群进行容器服务的统一管理。Swarm的架构中的角色为Manager及Worker。目前主流的容器编排方案为Docker Swarm加Compose或者为Kubernetes。 Docker Swarm集群配置 -1. 查看自身主机于其他Docker主机可以通讯的公网或者私网IP -2. 初始化Swarm集群并公布Manager节点的IP地址 docker swarm init --advertise-addr 172.18.49.120 -3. Worker节点通过Manager节点的IP地址进行加入 docker swarm join --token SWMTKN-1-09tuj2uw2h2ajbkq1n1ya6g2n7g496fk7ckiwf9e9plhcpgsut-1g0hm1mduwlbnzf36kahes38k 172.18.49.120:2377 -4. 加入后查看Swarm集群内所有节点 docker node ls -5. 通过Docker Info可见到集群部分信息 docker info Docker Swarm服务管理 -1. 创建Wordpress服务并指定副本数量为10 docker service create --replicas 10 --name wordpress wordpress:latest -2. 查看服务中容器的运行情况 docker service ps wordpress -3. 查看服务详细信息 docker service inspect wordpress -4. 创建服务并映射容器端口到Docker主机上 docker service create --name=wordpress-service -p 8080:80 --replicas 2 wordpress:latest -5. 服务中容器副本伸缩 docker service scale wordpress-service=5 -6. 删除服务 docker service rm wordpress-service -7. 滚动更新根据旧版本的镜像创建服务 docker service create --name=wordpress-service -p 80:80 --replicas=2 wordpress:php7.1-fpm 更新服务的镜像为最新 docker service update --image wordpress:latest wordpress-service wordpress-service

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

Docker之10 Docker Compose

什么是Docker Compose Compose是一个用于定义和运行多容器Docker应用程序的工具。Docker Compose可以使用YAML文件来配置应用程序的服务,接着就可以从配置中创建并启动所有服务。 如何配置Docker Compose 很简单,将docker-compose在github下载下来赋予执行权限即可运行。 curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose Docker Compose 管理命令 查看容器列表docker-compose ps查看服务日志docker-compose log打印绑定端口docker-compose port构建服务docker-compose build 启动已存在的容器服务docker-compose start 停止已运行的容器服务docker-compose stop 删除服务容器dokcer-compose rm 构建或启动容器docker-compose up停止服务容器docker-compose kill下载服务镜像docker-compose pull设置服务运行的容器数量docker-compose scale在服务器上运行命令docker-compose run Docker Compose Yaml File 指令格式 version:指定 docker-compose.yml 文件的写法格式 services:多个容器集合 environment:环境变量配置,可以用数组或字典两种方式 restart:容器在关闭后是否重启 depends_on:定义服务依赖关系 image:指定服务所使用的镜像 network_mode:设置网络模式 ports:对外暴露的端口定义,和 expose 对应 volumes:卷挂载路径 一个完整的WordPress例子 version: '3.3' services: db: image: mysql:5.7 volumes: - db_data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: somewordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress wordpress: depends_on: - db image: wordpress:latest ports: - "8000:80" restart: always environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress WORDPRESS_DB_NAME: wordpress volumes: db_data: {} docker-compose up -d docker-compose ps http://ServerName:8000

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

Spring Cloud Gateway 之 Filter

文章首发于公众号《程序员果果》地址:https://mp.weixin.qq.com/s/CFqKtIzP1ZzjioVZyGugeQ 简介 网关经常需要对路由请求进行过滤,进行一些操作,如鉴权之后构造头部之类的,过滤的种类很多,如增加请求头、增加请求 参数 、增加响应头和断路器等等功能,这就用到了Spring Cloud Gateway 的 Filter。 作用 当我们有很多个服务时,比如下图中的user-service、goods-service、sales-service等服务,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。 对于这样重复的工作,可以在微服务的上一层加一个全局的权限控制、限流、日志输出的Api Gateway服务,然后再将请求转发到具体的业务服务层。这个Api Gateway服务就是起到一个服务边界的作用,外接的请求访问系统,必须先通过网关层。 生命周期 Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。 PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 分类 Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。 GatewayFilter:应用到单个路由或者一个分组的路由上。 GlobalFilter:应用到所有的路由上。 Gateway filter 过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。过滤器的作用域为特定路由。Spring Cloud Gateway包含许多内置的GatewayFilter工厂。 官方文档都给出了这些过滤器工厂详细的使用案例,在这里我们讲解2个来演示下使用。 AddRequestHeader GatewayFilter Factory application.yml如下: spring: cloud: gateway: routes: - id: add_request_header_route uri: http://httpbin.org:80/get filters: - AddRequestHeader=X-Request-Foo, Bar predicates: - Method=GET 过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Foo,值为Bar。 RewritePath GatewayFilter Factory 在Nginx服务启中有一个非常强大的功能就是重写路径,Spring Cloud Gateway默认也提供了这样的功能,这个功能是Zuul没有。 application.yml如下: spring: cloud: gateway: routes: - id: rewritepath_route uri: http://httpbin.org predicates: - Path=/foo/** filters: - RewritePath=/foo/(?<segment>.*), /$\{segment} 所有的/foo/**开始的路径都会命中配置的router。请求http://httpbin.org/foo/get ,会转到http://httpbin.org/get,结果如下: { "args": { }, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8", "Cache-Control": "max-age=0", "Cookie": "Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1550127915; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22168eada0ded53b-0d5d8c3ba9b7a2-10316653-1296000-168eada0dee3ad%22%2C%22%24device_id%22%3A%22168eada0ded53b-0d5d8c3ba9b7a2-10316653-1296000-168eada0dee3ad%22%2C%22props%22%3A%7B%7D%7D; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1", "Forwarded": "proto=http;host=\"127.0.0.1:8080\";for=\"127.0.0.1:62278\"", "Host": "httpbin.org", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36", "X-Forwarded-Host": "127.0.0.1:8080", "X-Forwarded-Prefix": "/foo" }, "origin": "127.0.0.1, 124.74.78.150, 127.0.0.1", "url": "https://127.0.0.1:8080/get" } 自定义GatewayFilter Spring Cloud Gateway内置了的过滤器工厂,足够是大部分场景使用,而且我们可以实现GatewayFilter和Ordered 这两个接口来自定义过滤器。代码如下: /** * 统计某个或者某种路由的处理时长 */ public class CustomerGatewayFilter implements GatewayFilter, Ordered { private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilter.class ); private static final String COUNT_START_TIME = "countStartTime"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put(COUNT_START_TIME, Instant.now().toEpochMilli() ); return chain.filter(exchange).then( Mono.fromRunnable(() -> { long startTime = exchange.getAttribute(COUNT_START_TIME); long endTime=(Instant.now().toEpochMilli() - startTime); log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms"); }) ); } @Override public int getOrder() { return 0; } } 上述代码中,getOrder()方法是来给过滤器设定优先级别的,值越大则优先级越低。需要将自定义的GatewayFilter 注册到router中,代码如下: @Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(r -> r.path("/customer/**") .filters(f -> f.filter(new CustomerGatewayFilter()) .addResponseHeader("X-Response-test", "test")) .uri("http://httpbin.org:80/get") .id("customer_filter_router") ) .build(); } 使用 curl 测试,命令行输入: curl http://localhost:8080/customer/555 控制台输出如下: 2019-02-22 10:39:47.840 INFO 1310 --- [ctor-http-nio-3] com.gf.config.CustomerGatewayFilter : /customer/555: 314ms 自定义过滤器工厂 自定义GatewayFilter又有两种实现方式,一种是上面的直接 实现GatewayFilter接口,另一种是 自定义过滤器工厂(继承AbstractGatewayFilterFactory类) , 选择自定义过滤器工厂的方式,可以在配置文件中配置过滤器了。 @Component public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomerGatewayFilterFactory.Config> { private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class ); private static final String COUNT_START_TIME = "countStartTime"; @Override public List<String> shortcutFieldOrder() { return Arrays.asList("enabled"); } public CustomerGatewayFilterFactory() { super(Config.class); log.info("Loaded GatewayFilterFactory [CustomerGatewayFilterFactory]"); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { if (!config.isEnabled()) { return chain.filter(exchange); } exchange.getAttributes().put(COUNT_START_TIME, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(COUNT_START_TIME); if (startTime != null) { StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath()) .append(": ") .append(System.currentTimeMillis() - startTime) .append("ms"); sb.append(" params:").append(exchange.getRequest().getQueryParams()); log.info(sb.toString()); } }) ); }; } public static class Config { /** * 控制是否开启统计 */ private boolean enabled; public Config() {} public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } } } application.yml 中 网关路由配置如下: spring: profiles: elapse_route cloud: gateway: routes: - id: elapse_route uri: http://httpbin.org:80/get filters: - Customer=true predicates: - Method=GET 使用 curl 测试,命令行输入: curl http://localhost:8080/customer?foo=1 控制台输出如下: 2019-02-23 13:37:08.769 INFO 1700 --- [ctor-http-nio-3] c.g.config.CustomerGatewayFilterFactory : /customer: 585ms params:{foo=[1]} Global filter Spring Cloud Gateway框架内置的GlobalFilter如下: 内置的 GlobalFilter 能够满足大多数的需求了,但是如果遇到特殊情况,内置满足不了我们的需求,还可以自定义GlobalFilter。 自定义GlobalFilter 下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。 /** * Token 校验全局过滤器 */ @Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final Logger log = LoggerFactory.getLogger( AuthorizeFilter.class ); private static final String AUTHORIZE_TOKEN = "token"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst( AUTHORIZE_TOKEN ); if ( StringUtils.isBlank( token )) { log.info( "token is empty ..." ); exchange.getResponse().setStatusCode( HttpStatus.UNAUTHORIZED ); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; } } 使用 curl 测试,命令行输入: curl http://localhost:8080/customer?foo=1 控制台输出如下: token is empty ... 源码下载 : https://github.com/gf-huanchupk/SpringCloudLearning/tree/master/chapter13/springcloud-gateway-filter

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

Docker之9 Dockerfile优化

为什么要进行Dockerfile的优化? 在 Docker build 过程中,会占用 Dockerfile 并且在其自身临时的容器中一对一地运行每个可操作的指令。 结果是每个可操作的指令都有一个新映像层。Docker镜像应该是小而快的,如果不能构建一个良好的Dockerfile来帮助你提高构建缓存命中率,那么你的镜像构建过程将会变得相当的缓慢,并且镜像的体积将会很大。 如何进行优化Dockerfile 镜像体积最小化 -1. 基础镜像的选择:常用的 Linux 系统镜像一般有 ubuntu、centos、debian,其中debian 更轻量,而且适用的用途很广。在运行某种应用例如PHP时,在业务需要满足下尽量适用官方的Docker镜像。 -2. 减少镜像层数:在一个Dockerfile中每个命令都会在原来的基础上生成一层镜像,这毫无疑问将会对镜像增加体积,尽量将RUN命令进行组合,这将有效的减少镜像层数达到减少体积的效果。 #优化前 RUN wget jdk-8u191-linux-x64.tar.gz RUN tar zxvf jdk-8u191-linux-x64.tar.gz #优化后 RUN tar zxvf jdk-8u191-linux-x64.tar.gz && \ mv jdk1.8.0_191 /usr/local/jdk -3. 清除缓存和临时文件:在镜像构建过程中,可能会适用apt-get或yum等方式进行包的安装,在完成后系统会残留一些不必要的文件,或者适用源代码进行编译某些应用时,源代码文件夹将会存放在镜像中,在镜像构建的最后步骤,可以将这些不必要的文件进行删除。 RUN rm -rf /usr/local/src/* && \ yum clean all -4. 镜像服务化:当有多个服务时,分开多个镜像去运行这些服务,各司其职,例如在同一个镜像中打包了Apache httpd、MySQL、PHP,这无疑造成该镜像耦合性太差且体积臃肿。建议采用不同的镜像去托管不同的应用。 构建速度最快化 -5. 构建缓存在构建过程中,如果可以利用到缓存无疑将提升构建的速度,缓存的触发原则是单个指令的内容没有变化,则Docker构建时就会默认去使用缓存。我们可以通过将一些不变的操作独立成单独的指令,这样在构建时只会对有产生变化的指令构建,而不变的指令将会使用缓存。 -6. 删除构建目录额外的文件在构建镜像时将会上传构建文件夹中的所有文件,但有时不是全部的文件都是构建所需的,可以通过.dockerignore文件进行过滤,这将提高构建的速度及镜像的大小。 -7. 优化网络请求在构建镜像过程中,可能有指令需要使用网络去实现下载、安装等操作,由于网络的不确定性,可能会造成构建过程较久,而且构建的时间不一等问题,建议采用本地化引用网络资源来解决问题,例如Centos使用yum进行软件包的管理时,可以通过Nexus进行本地代理,从而减少网络请求的问题。 使用优化 -8. 使用CMD、VOLUME指令对Image进行服务化在不同的业务场景中会对镜像有不同的应用,但如果每次需要进行手动添加一些业务特定的东西时是比较繁琐的,可以采用CMD、VOLUME指令进行镜像的服务化弹性定制。 -9. 使用LABEL对镜像进行元数据维护在Docker镜像仓库中可能托管很多镜像,每一个镜像通途不一,但靠镜像名和标记是无法判断具体信息的,使用LABEL的方式可以对镜像的元数据进行管理,从而更清晰的知道镜像的信息。 -10. 使用变量就像编程一样,当一个字符串出现多次时,请用ARG来声明变量取代hard code。

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

Docker之7 仓库管理

什么是Docker Registry Docker Registry是一个私有镜像仓库服务,可存储和分发Docker镜像。 如何安装Docker Registry 使用Docker命令直接运行启动一个Registry容器即可 http 模式 docker run -d \ --restart=always \ --name registry \ -v /data/registry:/var/lib/registry \ -p 5000:5000 \ registry https 模式 申请SSL证书,在这里我们使用阿里云的免费SSL证书 将证书和私钥文件存储在Docker主机上 yekangming.com.key yekangming.com.pem --> Docker Host: /data/certs 将证书拷贝到Docker客户端上 yekangming.com.pem --> Docker Client: /etc/docker/certs.d/ docker run -d \ --restart=always \ --name registry \ -v /data/registry:/var/lib/registry \ -v /data/certs:/certs \ -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.yekangming.com.pem \ -e REGISTRY_HTTP_TLS_KEY=/certs/registry.yekangming.com.key \ -p 443:443 \ registry 上传镜像测试 docker tag centos:latest registry.yekangming.com/centos:latest docker push registry.yekangming.com/centos:latest 下载镜像测试 docker rmi registry.yekangming.com/centos:latest docker pull registry.yekangming.com/centos:latest

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

LINUX之Bash-Script

最近把Linux Bash编程的知识复习了一遍,大概梳理了一下,做个记录。 第一个Bash-Script LINUX shell的种类非常之多,但是目前用得最为广泛的还是Bash,本文也是基于Bash的Shell环境。 下面是一个简单的示例: #! /bin/sh echo 'hello world!' 这就是一个最简单的shell脚本了。 第一行的#!用来告诉系统,这个脚本用什么解释器来执行(说明:sh和bash本身是不同的Shell,但是在我目前用得CentOS7版本sh和bash是等价的,sh是一个指向bash的符号链接)。 echo命令输出文本到屏幕 如何运行脚本 一种方式就是将脚本作为解释器的参数,如: sh test.sh 第二种方式就是授予文件可执行权限 chmod +x test.sh 或者 chmod 755 test.sh 执行脚本 ./test.sh 变量与参数 变量 Bash是一种弱类型的语言,你只需要直接定义变量名=value即可。当需要引用这个变量的时候使用$var_name或者${var_name}即可。 $var_name是${var_name}的一种简写形式,但是在某些情况下只能使用${var_name},例如: # 当需要连接两个变量 your_id=${USER}-on-${HOSTNAME} # 或者会与其他字符串写在一起的时候 echo '${num}instance running!' 所以建议在写shell的时候统一使用${var_name}的风格。 bash中变量是弱类型的,那么不管你传给他的是字符串、数字或者数组他都统统会接收,有时候这就会导致一些问题,比如你想做个计算器,这时候传给了变量一个'ABCDEFG',那么这时候我们就想这个变量只能接收数字。又或者你想定义一个常量,不想用户修改。那么以上这些我们可以使用declare来声明变量的一些属性。 declare常用参数如下: -r 定义一个只读变量,也可以使用"readonly var_name"将变量定义为只读变量 -i 定义变量类型为整型 -a 定义变量类型为数组 -f 定义一个函数 -x 定义一个全局变量 当想删除一个变量的时候直接使用unset var_name即可,注意这个命令不能删除只读变量。 参数 那么如何向一个脚本传递参数呢? 当我们想向一个脚本传递参数的时候,直接在执行脚本的命令后面跟参数即可: sh ./test.sh parm1 parm2 parm3 在脚本中使用$n(n是一个数字,如:$1获取第一个参数)来接收传入的参数即可。下面是可能经常用到的参数变量: $0 当前脚本的文件名。 $n 传递给脚本或函数的参数。n是一个数字。例如,第一个参数是 $1 。 $# 传递给脚本或函数的参数个数。 $* 传递给脚本或函数的所有参数。 $@ 传递给脚本或函数的所有参数。被双引号 (" ")包含时与$*不同。 $? 上个命令的退出状态,或函数的返回值。 $_ 上一个命令的最后一个参数 我们经常用到的Linux命令中有一种叫option的东西,基本模式-optionname或者--longoptionname。如果在我们编写脚本的时候需要option的话,简单的脚本直接把他当普通参数手工处理即可,面对比较复杂的可以使用getopt或者getopt。 这里感觉有必要提一下set这个命令,我觉得很牛逼、很厉害、很有用但是我从来没在脚本中使用过的命令。 set命令用来改变内部脚本的变量或者选项。一种应用就是来改变options来影响脚本的行为。另一种应用就是利用set `commond` 的输出来重置位置参数。 Example #!/bin/bash echo "Command-line argument #1 = $1" echo "Command-line argument #2 = $2" echo "Command-line argument #3 = $3" echo "--------------------------------------" echo "利用`uname -a`的输出来重置位置参数" set `uname -a` echo "Field #1 of 'uname -a' = $1" echo "Field #2 of 'uname -a' = $2" echo "Field #3 of 'uname -a' = $3" exit 0 output: [root@localhost study]# sh test_set.sh one two three Command-line argument #1 = one Command-line argument #2 = two Command-line argument #3 = three -------------------------------------- Sets the positional parameters to the output of the commond `uname -a` Field #1 of 'uname -a' = Linux Field #2 of 'uname -a' = localhost.localdomain Field #3 of 'uname -a' = 3.10.0-514.el7.x86_64 数组 目前只支持一维数组!!!!!!!!!! 可以通过以下两种方式来为数组赋值: #!/bin/bash my_array=(A B C "D") my_array[4]=F my_array[5]="G" 通过${my_array[idx]}来读取数组中的值,也可以通过${my_array[*]}或${my_array[@]}来一次性读取数组中的所有元素。通过${#my_array[*]}或${#my_array[@]}来获取数组长度。 #!/bin/bash my_array=(A B C "D") my_array[4]=F my_array[5]="G123" echo "第一个元素:${my_array[0]}" echo "通过*来获取数组所有元素:${my_array[*]}" echo "通过@来获取数组所有元素:${my_array[@]}" echo "通过*来获取数组长度:${#my_array[*]}" echo "通过@来获取数组长度:${#my_array[@]}" output: [root@localhost study]# sh test_arr.sh 第一个元素:A 通过*来获取数组所有元素:A B C D F G123 通过@来获取数组所有元素:A B C D F G123 通过*来获取数组长度:6 通过@来获取数组长度:6 关于引号 反引号 反引号括起来的字符串被bash解释为命令行执行,并以它的输出结果取代整个反引号部分。 #! /bin/sh str=`echo $PATH` echo ${str} output: [root@localhost study]# sh qut.sh /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin 单引号 单引号扩起来的字符都视作普通字符,所有特殊字符都会失去原油的意义。 #! /bin/sh str='${PATH}' echo ${str} output: [root@localhost study]# sh qut.sh ${PATH} 双引号 由双引号括起来的字符,除$、\、’、和”这几个字符仍是特殊字符并保留其特殊功能外,其余字符作为普通字符对待。 while与for for循环 for循环的一般格式为 for var in [list] do commonds done 下面来看个例子: #!/bin/bash # 如果后面的list不加引号,则默认以空格分割 for var in A B C D do echo "${var}" done echo;echo # 会将引号内的内容当做一个元素 for var in "A A" "B B B" "C C" do echo "${var}" done echo;echo # 上面两种写法有点类似java的for-each # 如果你用过C,那么你对下面这种写法更熟悉 for ((a=1, b=1; a <= 5 ; a++, b++)) do echo -n "$a-$b " done output: [root@localhost study]# sh for-loop.sh A B C D A A B B B C C 1-1 2-2 3-3 4-4 5-5 while循环 while循环的一般格式为 while [ condition ] do commands done Example: #!/bin/bash while (( a <= 5 )) do echo -n "$a " let "a+=1" done break与continue break用来跳出整个循环,continue跳出本次循环。 算术运算 首先来看个例子: #! /bin/bash b=1+2 echo "${b}" output: [root@localhost study]# sh test.sh 1+2 默认情况下,bash中会把所有的赋值当做字符或字符串,所以看到的输出是1+2而不是3。 可以用以下三种方式来做算术运算。 #! /bin/bash # 1、使用`declare`来声明变量为整型。 declare -i b b=1+2 echo "declare:${b}" echo;echo # 2、let命令 let "c=1+1" echo "let:${c}" echo;echo # 3、利用双括号将表达式括起来 ((d=2+3)) echo "((表达式)):${d}" echo;echo # 4、利用单个方括号 echo "[表达式]:$[1+2]" output: [root@localhost study]# sh test.sh declare:3 let:2 ((表达式)):5 [表达式]:3 test命令 首先需要注意的是在Linux中“0”表示成功。test命令用来检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。 直接使用test这个命令 [...]:继承自test命令,主要用来方便其他语言的程序员。 ((...)): 返回其中表达式的计算结果 Example: [root@localhost ~]# test 1 -lt 2 [root@localhost ~]# echo $? 0 [root@localhost ~]# [ 1 -lt 2 ] [root@localhost ~]# echo $? 0 [root@localhost ~]# [[ 2 -eq 2 ]] [root@localhost ~]# echo $? 0 [root@localhost ~]# (( 2 == 2 )) [root@localhost ~]# echo $? 0 数字测试 数值测试操作符: -eq 或 == : 是否相等 -ne 或 != : 不等于 -gt 或 > : 大于 -lt 或 < : 小于 -ge 或 >= : 大于等于 -le 或 <= : 小于等于 注意:或选项的用法其实就是讲数字当做字符串来比较 Example: #! /bin/sh a=4 b=5 if [ ${a} -ne ${b} ] then echo "${a} is not equal to ${b} fi echo if [ ${a} != ${b} ] then echo "${a} is not equal to ${b}." fi 字符串测试 = : 判断两个字符串是否相等。if [ "$a" = "$b" ] == : 判断两个字符串是否相等,=的一个同义词 != : 判断字符串是否不等 < : 小于,根据ASCII的排序,注意在[ a < b ]需要转义,在[[ a < b ]]可以直接使用,>同理 : 大于,根据ASCII的排序 -z : 字符串是否为空,或长度为0 -n : 字符串是否非空 Example: #! /bin/sh str1="" if [ -z ${str1} ] then echo "str1 is null" fi echo str1="abc" if [ -n ${str1} ] then echo "str1 is not null" fi echo str2="cdef" if [ "${str1}" != "${str2}" ] then echo "${str1} is not equal to ${str2}" fi 文件测试 下面列举一些常用的文件操作: -e : 文件或目录是否存在 -f : 是否是一个文件(而非目录) -d : 是否是目录 -s : 文件大小是否为0 -r : 是否对文件有读权限 -w : 是否对文件有写权限 -x : 是否对文件有执行权限 -O : 是否是文件所有者 -G :是否与文件组用户相同 Example: #! /bin/sh filename="/study/bash/test_file.txt" if [ -e "${filename}" ] then echo "${filename} file exists." fi if [ -f "${filename}" ] then echo "${filename} is a regular file." fi if [ -s "${filename}" ] then echo "${filename} not a zero file." fi if & case if语句的一般格式: if condition then commonds fi if-else语句的格式 if condition1 then commonds elif condition2 then commonds else commonds fi case语句的一般格式: case 值 in 模式1) commonds ;; 模式2) commonds ;; esac 在case语句中可以用*号来匹配任意值。 函数 平时在一些简单的脚本里面基本用不到函数,但是在复杂的脚本中用来组织代码以及封装常用逻辑,在bash中还是很好用的东西。 函数的定义格式如下: [ function ] funname [()]{ action; [return ${result};] } 1、我们可以function funname方式来定义,也可以直接funname()。 2、可以显示的加return来返回结果或者直接返回最后一条命令的执行结果。 Example: #! /bin/sh fun(){ echo "it's a function" } fun 向函数传递参数的形式类似执行脚本的传参:fun_name arg1 arg2 Example: #! /bin/sh fun(){ if [ ${1} -gt ${2} ] then echo "${1} bigger than ${2}." elif [ ${2} -gt ${1} ] then echo "${2} bigger than ${1}." else echo "${1} equal to ${2}." fi } fun 10 20 读取键盘的输入 可以通过read命令来读取标准输入的值。下面用一个例子来看下在bash中read的常用方式: #! /bin/sh # 将读取到的值赋值给var1这个变量,当我们在行尾加\时,按回车会换到下一行继续输入 read var1 echo "var1 is ${var1}" echo "===============================" # 直接read,可以使用$REPLY来获取读取到的值 read echo "read $REPLY" echo "===============================" # -r参数会按字符串解释\,而不会换行 read -r mul_line echo "mul_line:${mul_line}" echo "===============================" # 上面的方式都会在屏幕上打印输入值,在交互式的bash中是不友好的,我们可以加-s选项来屏蔽输入的打印 read -s var2 echo "dont echo input:${var2}" echo "===============================" # 有时候可能不希望一直等待用户输入,我们可以用-t选项来这只超时时间 TIMELIMIT=5 read -t $TIMELIMIT var3 if [ -z ${var3}] then var3="time out" fi echo "timed read:${var3}" 下面是测试的输出 [root@localhost study]# sh read_test.sh read value to var2\ new line var1 is read value to var2new line =============================== read had no var read read had no var =============================== read with -r option\ mul_line:read with -r option\ =============================== dont echo input:do not echo input =============================== timed read:time out 字符串 在大多数编程语言中,字符串是非常重要、常用的数据类型。 1、字符串长度 ${#string} expr length $string expr "$string" : '.*' Example: #! /bin/sh str1="fdhjksdf" echo "${#str1}" echo "`expr length ${str1}`" echo "`expr "${str1}" : '.*'`" 2、匹配子串长度 expr match "$string" '$substr_regxp' expr "$string" : '$substr_regxp' Example: #!/bin/sh str1="str;str-str;strend" sub_regxp="[a-z]*;" echo "`expr match "${str1}" "${sub_regxp}"`" echo "`expr "${str1}" : "${sub_regxp}"`" 3、子串开始索引 expr index $string $substring Example #! /bin/sh str="abc;efg;higj" rgx_str=";.*;" echo "`expr index ${str} ${rgx_str}`" 4、字符串截取 ${string:position}截取从postition位置开始的字符串 ${string:position:length}从position位置开始截取长度为length长的子串,postition的开始索引为0 expr substr $string $position $length从position位置开始截取长度为length长的子串,postition的开始索引为1 expr match "$string" '$sub_regxp'截取匹配正则的子串 expr "$string" : '$sub_regxp'截取匹配正则的子串 Example #! /bin/sh str="abc;defg;hijk;lmnpq" echo "\${string:position}" echo "${str:4}" echo "===================================" echo "\${string:position:length}" echo "${str:4:4}" echo "===================================" echo "expr substr \$string \$position \$length" echo "`expr substr ${str} 5 4`" echo "===================================" sub_regxp=".*;\(.*\);" echo "expr match "\$string" '${sub_regxp}'" echo "`expr match "${str}" ${sub_regxp}`" echo "===================================" echo "expr "\$string" : '\$sub_regxp'" echo "`expr "${str}" : ${sub_regxp}`" output: [root@localhost study]# sh sub_str.sh ${string:position} defg;hijk;lmnpq =================================== ${string:position:length} defg =================================== expr substr $string $position $length defg =================================== expr match $string '.*;\(.*\);' hijk =================================== expr $string : '$sub_regxp' hijk 5、删除子串 ${string#substring},从前面最小匹配删除 ${string##substring},从前面最长匹配删除 ${string%substring},从后面最小匹配删除 ${string%%substring},从后面最小匹配删除 Example #! /bin/sh str=abcABC123ABCabc reg_str='a*C' echo ${str#$reg_str} # 123ABCabc echo ${str##$reg_str} # abc reg_str='b*c' echo ${str%$reg_str} # abcABC123ABCa echo ${str%%$reg_str} # a 6、子串替换 ${string/substring/replacement}从前面最小匹配替换 ${string//substring/replacement}从前面最长匹配替换 ${string/#substring/replacement}从后面最小匹配替换 ${string/%substring/replacement}从后面最长匹配替换 Debug your Script 在bash中提供了相关的参数供我们进行脚本的调试追踪。 sh [-nvx] script.sh 参数: -n:不执行基本,只检查语法问题 -v:执行脚本前,先将脚本内容输出到屏幕上 -x:将使用到的内容显示到屏幕上

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

Docker之6 网络管理

Docker网络概述 网络(network)是为了实现容器间或容器与外接进行通信,Docker提供了6种网络模式来解决不同场景下的连接方案,分别是bridge、host、overlay、macvlan、none、network plugin。 如何根据不同的业务场景选择不同的网络 如果使用容器互连时桥接网络是最佳的解决方案。当需要Docker主机隔离时主机网络最佳的解决方案。当需要跨Docker主机实现网络互联时覆盖网络是最佳选择。当您从大流量需要像物理网卡性能工作时Macvlan网络最佳选择,每个主机都具有唯一的MAC地址。第三方网络插件允许您将Docker与专用网络堆栈集成。 bridge网络 bridge模式是默认容器启动默认分配的网络方式,容器使用独立的网络命名空间(namespace),并连接到docker0虚拟网卡上。 host网络 host模式是容器与Docker主机共享同一网络命名空间(namespace),Docker主机的网络协议栈、路由表、iptables规则、网卡、IP、端口等等都是共享的。容器跟宿主机都在同一网络视图下。这个模式很好的解决了容器与外界通信地址转换的问题,可以直接使用宿主机的IP进行通信,那么这里的网络流量和压力走的都是宿主机的网卡,性能会比较高。不过这个有风险,因为容器跟宿主机是共享一套网络机制,没有隔离。那么会引起网络资源与宿主机的竞争和冲突关系。规模小的场景,可以使用这种模式。主机网络驱动程序仅适用于Linux主机,不支持Windows及Mac平台。 overlay网络 overlay模式是多个Docker主机之间的分布式网络解决方案。该网络位于特定于主机的网络之上,允许连接到它的容器(包括群集服务容器)安全地进行通信。Docker透明地处理每个数据包与Docker守护程序主机和正确的目标容器之间正确的的路由。 macvlan网络 macvlan跟overlay一样也是跨主机互联的驱动方案。在某些大流量或性能要求较高的场景下,希望直接连接到物理网络,在这种情况下,您可以使用macvlan网络驱动程序为每个容器的虚拟网络接口分配MAC地址,使其看起来像是直接连接到物理网络的物理网络接口。 none网络 none模式用于完全禁用容器上的网络堆栈,容器单独使用一个网络命名空间(namespace),仅创建回环设备,如果需要容器连接其他网络,则需要手动进行网络相关的设置,灵活性最高但也是最复杂。 插件网络 以上五种驱动都是docker原生提供的,如果以上五种不能满足你的要求,除了原生提供,还支持第三方的驱动模式接入。比如常用的 flannel、pipework、weave 和 calico 等等。 Docker network管理命令 显示网络列表 docker network ls 创建网络 docker network create <Option> <NetworkName> --config-from 复制其他网络配置 --driver 指定网络模式 --gateway 指定网关 --internal 限制只能内部访问 --ip-range 从子网范围分配容器IP --ipv6 启用IPv6网络 --subnet 指定网段 配置容器连接到指定的网络 docker network connect <Option> <Network> <Container> 取消容器连接到指定的网络 docker network disconnect <Option> <Network> <Container> 查看网络详情 docker network inspect <Network> 删除网络 docker network rm <Network> 清理未使用的网络 docker network prune 启动容器时指定网络 docker run -it --network=<Network> <Option> <Image> 采用网络名称进行通讯 docker network create lamp-network docker run -it --network lamp-network --network-alias apache centos:latest docker run -it --network lamp-network --network-alias php centos:latest docker run -it --network lamp-network --network-alias mysql centos:latest

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

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

Spring

Spring

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

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册