首页 文章 精选 留言 我的

精选列表

搜索[教程],共9643篇文章
优秀的个人博客,低调大师

Source Map入门教程

部署前端之前,开发者通常会对代码进行打包压缩,这样可以减少代码大小,从而有效提高访问速度。然而,压缩代码的报错信息是很难Debug的,因为它的行号和列号已经失真。这时就需要Source Map来还原真实的出错位置了。 为啥变换代码? 前端代码越来越复杂的情况下,开发者通常会使用webpack、UglifyJS2等工具对代码进行打包变换,这样可以减少代码大小,有效提高访问速度。关于变换代码的原因,这里不妨引用一下大神阮一峰的JavaScript Source Map 详解: 压缩,减小体积。比如jQuery 1.9的源码,压缩前是252KB,压缩后是32KB。 多个文件合并,减少HTTP请求数。 其他语言编译成JavaScript。最常见的例子就是CoffeeScript。 如何变换代码? 下面是一个简单的“hello World”程序hello.js function sayHello() { var name = "Fundebug"; var greeting = "Hello, " + Name; console.log(greeting); } sayHello(); 使用UglifyJS2对源代码进行压缩变换: uglifyjs hello.js \ -m toplevel=true \ -c unused=true,collapse_vars=true \ -o hello.min.js 压缩后的代码hello.min.js function o(){var o="Hello, "+Name;console.log(o)}o(); 为啥需要Source Map? 使用Firefox执行hello.js的报错信息是这样: ReferenceError: Name is not defined sayHello file:///Users/fundebug/sourcemap-tutorial/hello.js:4:9 <匿名> file:///Users/fundebug/sourcemap-tutorial/hello.js:8:1 而hello.min.js的报错信息是这样: ReferenceError: Name is not defined o file:///Users/fundebug/sourcemap-tutorial/hello.min.js:1:18 <匿名> file:///Users/fundebug/sourcemap-tutorial/hello.min.js:1:59 对比压缩前后的出错信息,我们会发现,错误行号和列号已经失真,且函数名也经过了变换。而对于真实的前端项目,开发者会将数十个源文件压缩为一个文件,这时,错误的列号可能多达数千,且出错的真实文件名也是很难确定的,这样的话,压缩代码的报错信息是很难Debug的。 而Source Map则可以用于还原真实的出错位置,帮助开发者更快的Debug。 什么是Source Map? 使用UglifyJS2时指定source-map选项即可生成Source Map: uglifyjs hello.js \ -m toplevel=true \ -c unused=true,collapse_vars=true \ --source-map hello.min.js.map \ --source-map-include-sources \ --source-map-root \ -o hello.min.js 各种主流前端任务管理工具,打包工具都支持生成Source Map,具体可以查看生成Source Map - Fundebug文档。 生成的hello.min.js多了sourceMappingURL,表示Source Map文件的位置。 function o(){var o="Hello, "+Name;console.log(o)}o(); //# sourceMappingURL=hello.min.js.map 生成的Source Map为hello.min.js.map: { "version": 3, "sources": ["hello.js"], "names": ["sayHello", "greeting", "Name", "console", "log"], "mappings": "AAAA,QAASA,KAEL,GACIC,GAAW,UAAYC,IAC3BC,SAAQC,IAAIH,GAGhBD", "file": "hello.min.js", "sourceRoot": "", "sourcesContent": ["function sayHello()\n{\n var name = \"Fundebug\";\n var greeting = \"Hello, \" + Name;\n console.log(greeting);\n}\n\nsayHello();\n"] } 由hello.min.js.map可知,Source Map是一个JSON文件,而它包含了代码转换前后的位置信息。也就是说,给定一个转换之后的压缩代码的位置,就可以通过Source Map获取转换之前的代码位置,反过来也一样。Source Map各个属性的含义如下: version:Source Map的版本号。 sources:转换前的文件列表。 names:转换前的所有变量名和属性名。 mappings:记录位置信息的字符串,经过编码。 file:(可选)转换后的文件名。 sourceRoot:(可选)转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空。 sourcesContent:(可选)转换前的文件内容列表,与sources列表依次对应。 Source Map真正神奇之处在于mappings属性,它记录了位置是如何对应的。JavaScript Source Map 详解已经有很好的解释,这里不再赘述。 怎样使用Source Map? 主流浏览器均支持Source Map功能,不过Chrome与Firefox需要一些简单的配置,具体步骤请参考How to enable source maps。下面以MacBook上的Chrome浏览器为例,介绍一下配置方法: 1. 开启开发者工具 使用快捷键option + command + i;或者在菜单栏选择视图->开发者->开发者工具 2. 打开设置 使用快捷键fn + F1;或者点击右上角的三个点的图标,选择Settings 3. 开启Source Map 在Sources中,选中Enable JavaScript source maps 为了测试,我写了一个简单的HTML文件hello.min.html <head> <script type="text/javascript" src="hello.min.js"></script> </head> 使用Chrome打开hello.min.html,在控制台看到的错误如下: Uncaught ReferenceError: Name is not defined at o (hello.min.js:1) at hello.min.js:1 报错的文件仍然为hello.min.js,需要刷新一下Source Map才有作用: Uncaught ReferenceError: Name is not defined at o (hello.js:4) at hello.js:8 注意,Chrome的报错信息没有列号,因此4为错误的行号。Chrome不仅可以通过Source Map还原真实的出错位置,还可以根据Source Map的sourcesContent还原出错的源代码。点击出错位置,即可跳转到源码,这样Debug将非常方便。 参考链接 JavaScript Source Map 详解 Source Map Revision 3 Proposal How to enable source maps 关于Fundebug Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了6亿+错误事件,得到了Google、360、金山软件等众多知名用户的认可。欢迎免费试用! 版权声明 转载时请注明作者Fundebug以及本文地址:https://blog.fundebug.com/2017/03/13/sourcemap-tutorial/

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

MyBatis框架教程「入门起步」

今天我们就踏上学习Mybatis框架的旅程,在SSM框架中Mybatis框架是dao层的一个解决方案。相当于传统Servlet+JavaBean开发模式中JDBC的作用。具体关于MVC架构的知识可以移步「从零学习Spring MVC框架「环境搭建和MVC架构」」文章,在这一篇文章的开篇我详细阐述了关于MVC架构的知识。 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs「Plain Old Java Objects,普通的 Java对象」映射成数据库中的记录,它是dao层的一个解决方案。 什么需求催生了MyBatis这种框架的产生呢?Mybatis框架比较重要的是:Mybatis自动将sql执行结果映射至java对象。我们知道目前流行的编程语言,例如Java,C#是面向对象的编程语言;但是主流的数据库产品,例如:Oracle,Mysql 等都是关系型数据库,编程语言和底层数据的发展不协调,催生出了ORM框架。我们可以理解为ORM框架可作为面向对象语言和数据库之间的桥梁。我们将要学习的MyBatis的设计思想和ORM很相似。 起步首先将mybatis-x.x.x.jar文件置于 classpath 中即可。由于需要连接数据库我们还需要驱动包。 如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中: <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency> 构建 SqlSessionFactory每个基于 MyBatis 的应用都是以一个 SqlSessionFactory的实例为中心的。SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder获得,关于SqlSession的介绍我们在文章末尾展开。 SqlSessionFactoryBuilder可以从 XML 配置文件或一个预先定制的 Configuration的实例构建出SqlSessionFactory的实例。SqlSession产生的第一步就是要利用SqlSessionFactoryBuilder来获取工厂SqlSessionFactory,再获得SqlSession。SqlSessionFactoryBuilder有五个build()方法,每一种都允许你从不同的资源中创建一个SqlSession实例。 SqlSessionFactory build(InputStream inputStream) SqlSessionFactory build(InputStream inputStream, String environment) SqlSessionFactory build(InputStream inputStream, Properties properties) SqlSessionFactory build(InputStream inputStream, String env, Properties props) SqlSessionFactorybuild(Configuration config) 以上五种 build()方法中,第一种是最常用的一种,在这里就不进行讲解。这里我们使用: //加载 核心配置文件 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); //创建sqlsession工厂 -->相当于connection SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //获取sqlsession -->相当于执行sql语句对象 sqlSession = sqlSessionFactory.openSession(); 3. mybatis-config.xml XML配置文件中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器。XML 配置文件的详细内容下方探讨,这里先给出一个简单的示例: <?xml version="1.0"encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTDConfig 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置环境 --> <environments default="jujidi"> <environment id="jujidi"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/dbname"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 加载映射文件 --> <mappers> <mapper resource="com/jujidi/test/TestMapper.xml" /> </mappers> </configuration> 当然,还有很多可以在XML 文件中进行配置,上面的示例指出的则是最关键的部分。要注意 XML 头部的声明,用来验证 XML 文档正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则是包含一组 mapper 映射器(这些 mapper 的 XML 文件包含了 SQL 代码和映射定义信息)关于事务管理器:在 MyBatis 中有两种类型的事务管理器JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文) TestMapper.xml <?xml version="1.0"encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.jujidi.model.User"> <!-- 通过id返回数据 --> <select id="load"resultType="map"> select * from user where user_id=1 </select> </mapper> namespace:是对此mapper 的唯一标识,通过select标签进行声明一个sql语句。 select中的id属性是在此mapper映射文件中sql语句的唯一id,我们可以通过namespace.id来定位这一条sql语句 MyBatisTest.java public class MybatisTest { public static void main(String[] args ) { SqlSession sqlSession = null; try{ //加载核心配置文件 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); //创建sqlsession工厂 -->相当于connection SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); //获取sqlsession -->相当于执行sql语句对象 sqlSession = sqlSessionFactory.openSession(); //执行sql Map<String , Object> map = sqlSession.selectOne("com.jujidi.model.User.load"); System.out.println(map); }catch(IOException e){ // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(sqlSession!=null){ sqlSession.close(); } } } } 到这里环境环境搭建完毕,讲解大体流程: MyBatisTest.java类中进行测试搭建的环境,首先加载核心配置文件mybatis-config.xml,此配置文件用来配置环境参数和加载映射文件。然后创建sqlsession工厂,这个工厂就要通过刚刚加载的mybatis-config.xml配置文件来build。通过工厂就可以生产sqlsession了,sqlsession有很多方法,我们执行selectOne方法来执行映射文件中的SQL,这就必须在selectOne方法中填写映射文件中对应SQL的地址,也就是全限定类名来定位。在映射文件中进行操作数据库把结果进行返回,打印输出。好了这里差不多把环境搭建完毕后的流程走了一遍,算是对MyBatis的一个入门和起步,接下来我们就来谈一下SqlSessionFactory和SqlSession. SqlSessionFactory和SqlSessionSqlSessionFactory是MyBatis非常重要的对象,他是单个数据库映射关系经过编译后的内存镜像。 SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder对象来获得,而SqlSessionFactoryBuilder从XML配置文件等中构建出SqlSessionFactory的实例。SqlSessionFactory一旦创建,在应用的执行期间都存在,其线程也是安全的。SqlSession类似于JDBC中的Connection。它是应用程序与持久层之间执行交互操作的一个单线程对象。它底层封装了JDBC连接,线程是不安全的。 原文发布时间为:2018-07-28本文作者:王久一本文来自云栖社区合作伙伴“Web项目聚集地”,了解相关信息可以关注“Web项目聚集地”。

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

Protocol Buffers入门教程

什么是Protocol Buffers 先看官网定义: protocol buffers – a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more. Protocol Buffers 是一种结构化数据的存储格式,可以用于结构化数据的序列化和反序列化。它很适合做数据存储、 RPC 数据交换格式。 Protocol Buffers的作用可以类比JSON、XML。 对于传输双方,如果约定好使用Protocol Buffer为数据传输的格式,那么这将是一种比JSON和XML都高效轻便的途径。 Protocol Buffers支持多种编程语言,包括C++、Java、Python、Go、C#等。 安装Protocol Buffers 1、官网下载压缩包 2、解压 tar -zxvf protobuf-all-3.6.0.tar.gz 3、protobuf的包需要自己编译 cd protobuf-3.6.0 ./configure make make install 4、在protobuf-3.6.0目录下有对应着各种语言的文件夹,每个文件夹下都有README.md,里面有相应的安装步骤。对于python: cd protobuf-3.6.0/python python setup.py build python setup.py test python setup.py install 5、到此为止,protobuf的python版本已经安装完成,可在命令行键入protoc试试。 Demo 1、创建addressbook.proto文件。.proto文件定义了要序列化的数据结构。 syntax = "proto2"; package tutorial; message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phones = 4; } message AddressBook { repeated Person people = 1; } 2、编译addressbook.proto protoc --python_out=/Users/ya/developer/protoc-test addressbook.proto 这一条命令执行完后会在你指定的目录下生成addressbook_pb2.py文件。 addressbook_pb2.py会提供关于所定义的数据结构的操作方法。 3、编写writer.py,负责序列化数据并保存到文件 # See README.txt for information and build instructions. import addressbook_pb2 import sys try: raw_input # Python 2 except NameError: raw_input = input # Python 3 # This function fills in a Person message based on user input. def PromptForAddress(person): person.id = int(raw_input("Enter person ID number: ")) person.name = raw_input("Enter name: ") email = raw_input("Enter email address (blank for none): ") if email != "": person.email = email while True: number = raw_input("Enter a phone number (or leave blank to finish): ") if number == "": break phone_number = person.phones.add() phone_number.number = number type = raw_input("Is this a mobile, home, or work phone? ") if type == "mobile": phone_number.type = addressbook_pb2.Person.MOBILE elif type == "home": phone_number.type = addressbook_pb2.Person.HOME elif type == "work": phone_number.type = addressbook_pb2.Person.WORK else: print("Unknown phone type; leaving as default value.") # Main procedure: Reads the entire address book from a file, # adds one person based on user input, then writes it back out to the same # file. if len(sys.argv) != 2: print("Usage:", sys.argv[0], "ADDRESS_BOOK_FILE") sys.exit(-1) address_book = addressbook_pb2.AddressBook() # Read the existing address book. try: with open(sys.argv[1], "rb") as f: address_book.ParseFromString(f.read()) except IOError: print(sys.argv[1] + ": File not found. Creating a new file.") # Add an address. PromptForAddress(address_book.people.add()) # Write the new address book back to disk. with open(sys.argv[1], "wb") as f: f.write(address_book.SerializeToString()) 5、执行writer.py python writer.py addressbook.data 执行完脚本后数据将会序列化并存储到addressbook.data中。 6、编写reader.py,负责读取文件并反序列化数据 import addressbook_pb2 import sys # Iterates though all people in the AddressBook and prints info about them. def ListPeople(address_book): for person in address_book.people: print "Person ID:", person.id print "Name:", person.name if person.email != "": print "E-mail address:", person.email for phone_number in person.phones: if phone_number.type == addressbook_pb2.Person.MOBILE: print "Mobile phone :", elif phone_number.type == addressbook_pb2.Person.HOME: print "Home phone :", elif phone_number.type == addressbook_pb2.Person.WORK: print "Work phone :", print(phone_number.number) # Main procedure: Reads the entire address book from a file and prints all # the information inside. if len(sys.argv) != 2: print("Usage:", sys.argv[0], "ADDRESS_BOOK_FILE") sys.exit(-1) address_book = addressbook_pb2.AddressBook() # Read the existing address book. with open(sys.argv[1], "rb") as f: address_book.ParseFromString(f.read()) ListPeople(address_book) 7、执行reader.py python reader.py addressbook.data 输出结果示例: Person ID: 1 Name: kanon E-mail address: 540004716@qq.com Mobile phone : 123456 两个很重要的方法 SerializeToString( ):serializes the message and returns it as a string. ParseFromString( data ):parses a message from the given string. Protocol Buffers 相比 XML are simpler are 3 to 10 times smaller are 20 to 100 times faster are less ambiguous generate data access classes that are easier to use programmatically 在ODPS(MaxCompute)中的应用 ODPS的Tunnel HTTP Server采用了Protobuf作为其序列化机制。客户端在上传数据时,需要先对结构化数据进行序列化(生成二进制流),Tunnel服务端接收到数据后,(对二进制流)执行反序列化,还原出结构化数据,写到ODPS表中。

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

Memcached安装教程及使用

Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载 Table of contents 安装 使用 在spring中使用 安装 下载下来memcached.exe 切换到memcached.exe所在路径 输入memcached -d install win + r 输入 services.msc打开window服务 随便选中一个输入memcached就可以查看到安装好的服务,右击启动它,然后关闭窗口 使用 新建java工程或者maven工程 导入三个必备的依赖,fastjson-1.2.3,slf4j-api-1.7.5,xmemcached-2.3.2 main方法中加入 MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("127.0.0.1:11211")); builder.setSessionLocator(new KetamaMemcachedSessionLocator()); try { MemcachedClient memcachedClient =builder.build(); //在这里写入测试代码 memcachedClient.shutdown(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (MemcachedException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } 测试一定时间后数据是否还能取出来 永久时间,测试重启服务之后先前存储的数据是否还能取出来 delete 自动增长 时效 memcachedClient.set("testTime",2,"testTimeValue"); String testTime = memcachedClient.get("testTime"); System.out.println("testTime = " + testTime); TimeUnit.SECONDS.sleep(4); System.out.println("4s 过去了"); String valueExist = memcachedClient.get(testTime); System.out.println("valueExist = " + valueExist); 输出: testTime = testTimeValue 4s 过去了 valueExist = null 恢复 //存储,然后关闭掉服务 memcachedClient.set("test",0,"testValue"); String testTime=memcachedClient.get("test"); System.out.println("testTime="+testTime); System.out.println("存储成功"); 输出: testTime = testValue 存储成功 //关闭掉服务后的重启 String testTime = memcachedClient.get("test"); System.out.println("testTime = " + testTime); System.out.println("取值失败"); 输出: testTime = null 取值失败 delete memcachedClient.set("test",0,"testValue"); String getVal = memcachedClient.get("test"); System.out.println("getVal = " + getVal); memcachedClient.delete("test"); System.out.println("after delete ..."); getVal = memcachedClient.get("test"); System.out.println("getVal = " + getVal); 输出: getVal = testValue after delete ... getVal = null 自动增长 //三个参数,第一个指定键,第二个指定递增的幅度大小,第三个指定当key不存在的情况下的初始值 for (int i = 0; i < 5; i++) { memcachedClient.incr("博客的赞",1,20); String point = memcachedClient.get("博客的赞"); System.out.println("point = " + point); } 输出: point = 20 point = 21 point = 22 point = 23 point = 24 关于incr的用法,值得警惕的是,它的值虽然看起来是一个数字,实际上正如代码中的String point = memcachedClient.get("博客的赞"); 其实是一个字符串,所以会出现如下错误 memcachedClient.set("博客的赞1",0,10); int str = memcachedClient.get("博客的赞1"); System.out.println("str1 = " + str); memcachedClient.incr("博客的赞1",2,22); str = memcachedClient.get("博客的赞1"); System.out.println("str2 = " + str); 输出: net.rubyeye.xmemcached.exception.MemcachedClientException: cannot increment or decrement non-numeric value,key=博客的赞1 at net.rubyeye.xmemcached.command.Command.decodeError(Command.java:267) .. at com.google.code.yanf4j.nio.impl.NioController.onRead(NioController.java:157) at com.google.code.yanf4j.nio.impl.Reactor.dispatchEvent(Reactor.java:323) at com.google.code.yanf4j.nio.impl.Reactor.run(Reactor.java:180) str1 = 10 输出的顺序不同,注意输出的异常栈信息的第一条和后面的几条就指明nio.impl.Reactor.run,线程的,这儿就不深入展开了 spring 新建一个maven工程 pom.xml 在resource中新建sping-config.xml spring单元测试代码骨架 测试代码 骨架 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-config.xml") public class TestMem { @Autowired private MemcachedClient memcachedClient; @Test public void test1(){ //测试代码部分 } } 测试代码 memcachedClient.set("springData",3,"dataVal"); String str = memcachedClient.get("springData"); System.out.println("str = " + str); 输出: str = dataVal 还可以存储对象,不过该对象必须实现Serializable接口,不然会报错java.io.NotSerializableException: Teacher, 实现接口后 import lombok.Data; import java.io.Serializable; @Data public class Teacher implements Serializable { private int age; private String name; } 关于@Data关我在另一篇博客中有介绍lombok Teacher teacher = new Teacher(); teacher.setAge(3); teacher.setName("23"); memcachedClient.set("te", 0, teacher); Teacher teacher1 = memcachedClient.get("te"); System.out.println("teacher1 = " + teacher1); 输出: teacher1 = Teacher(age=3, name=23) pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.selton</groupId> <artifactId>DemoMemSpring</artifactId> <version>1.0</version> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.11.RELEASE</version> </dependency> <dependency> <groupId>com.googlecode.xmemcached</groupId> <artifactId>xmemcached</artifactId> <version>2.0.0</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.11.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> </project> config <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <bean id="memcachedClient" name="memcachedClient" class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean"> <property name="servers"> <!--配置端口,另加入的话,空格隔开--> <value>127.0.0.1:11211</value> </property> <property name="weights"> <list> <!--设置不同端口的权重,这里只有一个端口--> <value>1</value> </list> </property> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" /> </property> <property name="bufferAllocator"> <bean class="net.rubyeye.xmemcached.buffer.SimpleBufferAllocator"></bean> </property> </bean> </beans>

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

Tensorflow快餐教程(9) - 卷积

卷积 卷积就是滑动中提取特征的过程 在数学中,卷积convolution是一种函数的定义。它是通过两个函数f和g生成第三个函数的一种数学算子,表征函数f与g经过翻转和平移的重叠部分的面积。其定义为:$h(x)=f(x)*g(x) =\int_{-\infty}^{\infty}f(t)g(x-t)dt$也可以用星号表示:$h(x)=(f*g)(x)$卷积的第一个参数(上例中的f),通常叫做输入。第二个参数(函数g)叫做核函数kernel function。输出有时候叫特征映射feature map.也可以定义离散形式的卷积:$h(x)=(f*g)(x) = \sum_{t=-\infty}^{\infty}f(t)g(x-t)$ g(x-t)是变化的,而f(t)是固定不动的。我们可以将卷积理解成是g(x-t)滑动过程中对f(t)进行采

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

Tensorflow快餐教程(4) - 矩阵

矩阵 矩阵的初始化 矩阵因为元素更多,所以初始化函数更多了。光靠tf.linspace,tf.range之类的线性生成函数已经不够用了。 可以通过先生成一个线性序列,然后再reshape成一个矩阵的方式来初始化。 例: >>> g1 = tf.linspace(1.0,10.0,16) >>> g1 <tf.Tensor 'LinSpace_6:0' shape=(16,) dtype=float32> >>> g2 = tf.constant(sess.run(tf.reshape(g1,[4,4]))) >>> sess.run(g2) array([[ 1. , 1.6 , 2.2 , 2.8000002],

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

Tensorflow快餐教程(3) - 向量

向量 向量在编程语言中就是最常用的一维数组。二维数组叫做矩阵,三维以上叫做张量。 向量虽然简单,高效,且容易理解。但是与操作0维的标量数据毕竟还是不同的。比如向量经常用于表示一个序列,生成序列像标量一样一个一个手工写就不划算了。当然可以用循环来写。在向量中这样还好,如果是在矩阵或者是张量中就强烈建议不要用循环来做了。系统提供的函数一般都是经过高度优化的,而且可以使用GPU资源来进行加速。我们一方面尽可能地多使用系统的函数,另一方面也不要迷信它们,代码优化是一个实践的过程,可以实际比较测量一下。 快速生成向量的方法 range函数生成等差数列 tf.range函数用来快速生成一个等差数列。相当于之前我们讲numpy时的np.arange函数。 原型: tf.range(start, limit, delta=1, dtype=None, name

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

Java 8 简明教程

文章概览 默认接口方法 Lambda表达式 方法引用 重复注解 简单介绍 接口中有默认方法 @FunctionalInterface interface Formula { double calculate(int a); default double sqrt(int a) { return Math.sqrt(a); } } 注意: //接口前加上@FunctionalInterface 标注,方便使用lambda //加这个注解,java可以很好的向下兼容jdk的版本 //如果你不写@FunctionalInterface 标注,程序也是正确的。 默认方法是为了实现函数式编程,lambda表达式 “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。 @FunctionalInterface interface Converter<F, T> { T convert(F from); } Converter<String, Integer> converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted); // 123 lamdba格式: (参数列表)->{ 方法体} ():跟元方法参数一致,都是小括号抱住 {}:方法体,跟原来方法一致,都是大括号抱住 -> :箭头,连接方法参数和方法体,表示一个函数 方法参数为一个时,小括号可以省略不写 方法体为一行代码时,大括号可以省略不写 lamdba在java中本质是一个静态方法,java编译成方法。 lamdba表达式可以简写为方法引用。 Converter<String, Integer> converter = Integer::valueOf; Integer converted = converter.convert("123"); System.out.println(converted); // 123 通过::关键字获取方法或者构造函数的的引用 引用构造函数 PersonFactory<Person> personFactory = Person::new; Person person = personFactory.create("Peter", "Parker"); 通过Person::new来创建一个Person类构造函数的引用。Java编译器会自动地选择合适的构造函数来匹配PersonFactory.create函数的签名,并选择正确的构造函数形式。 Lambda的范围 对于lambdab表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。你能够访问局部对应的外部区域的局部final变量,以及成员变量和静态变量。 但是与匿名对象不同的是,变量num并不需要一定是final,num在编译的时候被隐式地当做final变量来处理。下面的代码依然是合法的: int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 java8中内置了很多函数式接口 具体的查看官方文档 简单示例如下: 遍历map map.forEach((k, v) -> System.out.println(v)); 遍历list list.forEach((m) -> System.out.println(m)); Annotations 重复注解 Java 8中的注解是可重复的。让我们直接深入看看例子,弄明白它是什么意思。 首先,我们定义一个包装注解,它包括了一个实际注解的数组 @interface Hints { Hint[] value(); } @Repeatable(Hints.class) @interface Hint { String value(); } 只要在前面加上注解名:@Repeatable,Java 8 允许我们对同一类型使用多重注解, 变体1:使用注解容器(老方法) @Hints({@Hint("hint1"), @Hint("hint2")}) class Person {} 变体2:使用可重复注解(新方法) @Hint("hint1") @Hint("hint2") class Person {} 使用变体2,Java编译器能够在内部自动对@Hint进行设置。这对于通过反射来读取注解信息来说,是非常重要的。 Hint hint = Person.class.getAnnotation(Hint.class); System.out.println(hint); // null Hints hints1 = Person.class.getAnnotation(Hints.class); System.out.println(hints1.value().length); // 2 Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class); System.out.println(hints2.length); // 2 尽管我们绝对不会在Person类上声明@Hints注解,但是它的信息仍然可以通过getAnnotation(Hints.class)来读取。并且,getAnnotationsByType方法会更方便,因为它赋予了所有@Hints注解标注的方法直接的访问权限。 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface MyAnnotation {} 详细看这里:http://blog.didispace.com/books/java8-tutorial/ch1.html

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

Docker Swarm学习教程【转载】

Swarm介绍 Swarm是Docker公司在2014年12月初发布的一套较为简单的工具,用来管理Docker集群,它将一群Docker宿主机变成一个单一的,虚拟的主机。Swarm使用标准的Docker API接口作为其前端访问入口,换言之,各种形式的Docker Client(dockerclient in go, docker_py, docker等)均可以直接与Swarm通信。Swarm几乎全部用Go语言来完成开发,上周五,4月17号,Swarm0.2发布,相比0.1版本,0.2版本增加了一个新的策略来调度集群中的容器,使得在可用的节点上传播它们,以及支持更多的Docker命令以及集群驱动。 Swarm deamon只是一个调度器(Scheduler)和路由器(router),Swarm自己不运行容器,它只是接受docker客户端发送过来的请求,调度适合的节点来运行容器,这意味着,即使Swarm由于某些原因挂掉了,集群中的节点也会照常运行,当Swarm重新恢复运行之后,它会收集重建集群信息。下面是Swarm的结构图: 如何使用Swarm 有3台机器,sclu083,IP地址是10.13.181.83,sclu084,IP地址是10.13.181.84,atsg124 ,IP地址是10.32.105.124,利用这三台机器创建一个Docker集群,其中sclu083同时充当swarm manager管理集群. Swarm安装 最简单的安装Swarm的方式就是用Docker官方提供的Swarm镜像: sudo docker pull swarm Docker集群管理需要服务发现(Discovery service backend)功能.Swarm支持以下几种discovery service backend:Docker Hub上面内置的服务发现功能,本地的静态文件描述集群(static file describing the cluster),etcd(顺带说一句,etcd这玩意貌似很火很有前途,有时间研究下),consul,zookeeper和一些静态的ip列表(a static list of ips).本文会详细介绍前面两种方法backend的使用。 在使用Swarm进行集群管理之前,需要先把准备加入集群的所有的节点的docker deamon的监听端口修改为0.0.0.0:2375,可以直接使用 sudo docker –H tcp://0.0.0.0:2375 &命令,也可以在配置文件中修改 sudo vim /etc/default/docker 在文件的最后面添加下面这句 D0OCKER_OPTS=”-H 0.0.0.0:2375 –H unix:///var/run/docker.sock” 注意:一定是要在所有的节点上进行修改,修改之后要重启docker deamon sudo service docker restart 第一种方法:使用Docker Hub上面内置的服务发现功能 第一步:在任何一台节点上面执行swarm create命令来创建一个集群标志。这条命令执行完毕之后,swarm会前往Docker Hub上内建的发现服务中获取一个全球唯一的token,用以唯一的标识swarm管理的Docker集群。 sudo docker run –-rm swarm create 我们在sclu084 这台机器上执行上面的命令,效果如下: 返回的token是d947b55aa8fb9198b5d13ad81f61ac4d,这个token一定要记住,因为接下来的操作都会用到这一个token。 第二步:在所有的要加入集群的机器上面执行swarm join命令,把机器加入集群 本次试验就是要在所有的三台机器上执行命令: sudo docker run –-rm swarm join –addr=ip_address:2375 token://d947b55aa8fb9198b5d13ad81f61ac4d 在IP地址为10.13.181.84机器上面执行的效果如下图: 执行这条命令后不会立即返回 ,我们手动通过Ctrl+C返回。 第三步:启动swarm manager 因为我们是要让sclu083充当Swarm管理节点,所以我们要在这条机器上面执行swarm manage这条命令: sudo docker run –d –p 2376:2375 swarm manage token:// d947b55aa8fb9198b5d13ad81f61ac4d 需要注意的是:在这条命令中,第一:要以daemon的形式运行swarm。第二:端口映射:2376可以更换成任何一个本机没有占用的端口,一定不能是2375。否则就会出问题。 执行结果如下如所示: 执行完这个命令之后,整个集群已经启动起来了。 现在可以在任何一台节点上查看集群上的所有节点了。 之后可以在任何一台安装了docker的机器上面通过命令(命令中要指明swarm maneger 机器的IP地址和端口)在这个集群上面运行Dcoker容器操作。 现在在10.13.181.85这台机器上面查看集群的节点的信息。info命令可以换成任何一个Swarm支持的docker命令,这些命令可以查看官方文档 sudo docker –H 10.13.181.83:2376 info 由上图的结果,我们可以发现一个问题:明明这个小集群中是有3个节点的,但是info命令只显示了2个节点。还缺少节点10.32.105.124。为什么会出现这个情况呢? 因为10.32.105.124这台机器没有设置上面的docker daemon监听0.0.0.0:2375这个端口,所以Swarm没办法吧这个节点加入集群中来。 在使用Docker Hub内置的发现服务时,会出现一个问题,就是使用swarm create时会出现 time="2015-04-21T08:56:25Z" level=fatal msg="Get https://discovery-stage.hub.docker.com/v1/clusters/d947b55aa8fb9198b5d13ad81f61ac4d: dial tcp: i/o timeout" 类似于这样的错误,不知道是什么原因,有待解决。 当使用Docker Hub内置的服务发现功能出现问题时,可以使用下面的第二种方法。 第二种方法:使用文件 第二种方法相对而言比第一种方法要简单,也更不容易出现timeout的问题。 第一步:在sclu083这台机器上新建一个文件,把要加入集群的机器的IP地址写进去 第二步:在sclu083这台机器上面执行swarm manage命令: sudo docker run –d –p 2376:2375 –v $(pwd)/cluster:/tmp/cluster swarm manage file:///tmp/cluster 注意:这里一定要使用-v命令,因为cluster文件是在本机上面,启动的容器默认是访问不到的,所以要通过-v命令共享。还有,file:///千万不能忘记了 可以看到,swarm已经运行起来了。现在可以查看下集群节点信息了,使用命令: sudo docker run –rm –v $(pwd)/cluster:/tmp/cluster swarm list file:///tmp/cluster (在使用文件作为服务发现的时候,貌似manage list命令只能在swarm manage节点上使用,在其他节点上好像是用不了) 好了,现在集群也已经运行起来了,可以跟第一种方法一样在其他机器上使用集群了。同样在sclu085 机器上做测试: 可以看到,成功访问并且节点信息是正确的。接下来可以把上面的info命令替换成其他docker可执行命令来使用这个晓得Docker集群了。 Swarm调度策略 Swarm在schedule节点运行容器的时候,会根据指定的策略来计算最适合运行容器的节点,目前支持的策略有:spread,binpack,random. Random顾名思义,就是随机选择一个Node来运行容器,一般用作调试用,spread和binpack策略会根据各个节点的可用的CPU,RAM以及正在运行的容器的数量来计算应该运行容器的节点。 在同等条件下,Spread策略会选择运行容器最少的那台节点来运行新的容器,binpack策略会选择运行容器最集中的那台机器来运行新的节点(The binpack strategy causes Swarm to optimize for the container which is most packed.)。 使用Spread策略会使得容器会均衡的分布在集群中的各个节点上运行,一旦一个节点挂掉了只会损失少部分的容器。 Binpack策略最大化的避免容器碎片化,就是说binpack策略尽可能的把还未使用的节点留给需要更大空间的容器运行,尽可能的把容器运行在一个节点上面。 Constraint Filter 通过label来在指定的节点上面运行容器。这些label是在地洞docker daemon时指定的,也可以卸载/etc/default/docker这个配置文件里面。 sudo docker run –H 10.13.181.83:2376 run –name redis_083 –d –e constraint:label==083 redis Affinity Filter 使用-e affinity:container==container_name / container_id –-name container_1可以让容器container_1紧挨着容器container_name / container_id执行,也就是说两个容器在一个node上面执行(You can schedule 2 containers and make the container #2 next to the container #1.) 先在一台机器上启动一个容器 sudo docker -H 10.13.181.83:2376 run --name redis_085 -d -e constraint:label==085 redis 接下来启动容器redis_085_1,让redis_085_1紧挨着redis_085容器运行,也就是在一个节点上运行 sudo docker –H 10.13.181.83:2376 run –d –name redis_085_1 –e affinity:container==redis_085 redis 通过-e affinity:image=image_name命令可以指定只有已经下载了image_name的机器才运行容器(You can schedule a container only on nodes where the images are already pulled) 下面命令在只有redis镜像的节点上面启动redis容器: sudo docker –H 100.13.181.83:2376 run –name redis1 –d –e affinity:image==redis redis 下面这条命令达到的效果是:在有redis镜像的节点上面启动一个r名字叫做redis的容器,如果每个节点上面都没有redis容器,就按照默认的策略启动redis容器。 sudo docker -H 10.13.181.83:2376 run -d --name redis -e affinity:image==~redis redis Port filterPort也会被认为是一个唯一的资源 sudo docker -H 10.13.181.83:2376 run -d -p 80:80 nginx 执行完这条命令,任何使用80端口的容器都是启动失败。 结束语: 本文详细介绍了两种方法来使用Swarm管理Docker集群。但是Swarm是一个比较新的项目,目前还处于研发阶段,Swarm的发展十分快速,功能和特性的变更迭代还非常频繁。因此,可以说Swarm还不推荐被用于生产环境中,但可以肯定的是Swarm是一项很有前途的技术。 最近在学Go,准备抽时间好好研究下Swarm源码。Go是一门很有前途的语言。

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

Docker Toolbox 学习教程【转载】

最近在研究虚拟化,容器和大数据,所以从Docker入手,下面介绍一下在Windows下怎么玩转Docker。Docker本身在Windows下有两个软件,一个就是Docker,另一个是Docker Toolbox。这里我选择的是Docker Toolbox,为什么呢?参见官方文档:https://blog.docker.com/2015/08/docker-toolbox/首先我们从官网下载最新版的Windows Docker Toolbox。安装后会安装一个VirtualBox虚拟机,一个Kitematic,这是GUI管理Docker的工具,没有发布正式版,不推荐使用,另外还有就是我们在命令行下用到的docker-machine和docker命令了。 基本使用 安装完成Toolbox后会有一个Docker Quickstart Terminal的快捷方式,双击运行如果报错,那可能是因为你已经安装了Hyper-v,所以VirtualBox无法用64位的虚拟机。需要卸载Hyper-v。 运行后会在Virtualbox中创建一个叫做default的虚拟机,然后很有可能会卡在waiting for an IP的命令下,然后就死活不动了。我的做法是彻底放弃Docker Quickstart Terminal,根本不用这玩意儿,关掉,我们用PowerShell进行虚拟机的管理。 打开PowerShell,输入: docker-machine ls 我们可以看到我们当前的Docker虚拟机的状态。如果什么都没有的话,那么我们可以使用以下命令创建一个Docker虚拟机。 docker-machine create --driver=virtualbox default 创建完毕后,我们在用docker-machine ls确认我们的Docker虚拟机在运行中。 然后使用以下命令获得虚拟机的环境变量: docker-machine env default 然后再输入: docker-machine env default | Invoke-Expression 这样我们就把当前的PowerShell和虚拟机里面的Docker Linux建立的连接,接下来就可以在PowerShell中使用docker命令了。 比如我们要查看当前有哪些镜像: docker images 当前有哪些容器: docker ps –a 其他各种docker命令我就不在这里累述了。 Docker虚拟机文件地址修改 默认情况下,docker-machine创建的虚拟机文件,是保存在C盘的C:\Users\用户名\.docker\machine\machines\default 目录下的,如果下载和使用的镜像过多,那么必然导致该文件夹膨胀过大,如果C盘比较吃紧,那么我们就得考虑把该虚拟机移到另一个盘上。具体操作如下: 1.使用docker-machine stop default停掉Docker的虚拟机。 2.打开VirtualBox,选择“管理”菜单下的“虚拟介质管理”,我们可以看到Docker虚拟机用的虚拟硬盘的文件disk。 3.选中“disk”,然后点击菜单中的“复制”命令,根据向导,把当前的disk复制到另一个盘上面去。 4.回到VirtualBox主界面,右键“default”这个虚拟机,选择“设置”命令,在弹出的窗口中选择“存储”选项。 5.把disk从“控制器SATA”中删除,然后重新添加我们刚才复制到另外一个磁盘上的那个文件。 这是我设置好后的界面,可以看到我在步骤3复制的时候,复制到E:\VirtualBox\default\dockerdisk.vdi文件去了。 6.确定,回到PowerShell,我们使用docker-machine start default就可以启动新地址的Docker虚拟机了。确保新磁盘的虚拟机没有问题。就可以把C盘那个disk文件删除了。 【注意:不要在Window中直接去复制粘贴disk文件,这样会在步骤5的时候报错的,报错的内容如下,所以一定要在VirtualBox中去复制!】 Failed to open the hard disk file D:\Docker\boot2docker-vm\boot2docker-vm.vmdk. Cannot register the hard disk 'D:\Docker\boot2docker-vm\boot2docker-vm.vmdk' {9a4ed2ae-40f7-4445-8615-a59dccb2905c} because a hard disk C:\Users\用户名\.docker\machine\machines\default\disk.vmdk' with UUID {9a4ed2ae-40f7-4445-8615-a59dccb2905c} already exists. Result Code: E_INVALIDARG (0x80070057) Component: VirtualBox Interface: IVirtualBox {fafa4e17-1ee2-4905-a10e-fe7c18bf5554} Callee RC: VBOX_E_OBJECT_NOT_FOUND (0x80BB0001) 镜像加速 在国内使用Docker Hub的话就特别慢,为此,我们可以给Docker配置国内的加速地址。我看了一下,DaoCloud和阿里云的镜像加速还不错,另外还有网易的蜂巢。选一个就行了。以DaoClound为例,注册账号,然后在https://www.daocloud.io/mirror就可以看到DaoClound提供给您的镜像加速的URL。然后到PowerShell中去依次执行: docker-machine ssh default sudo sed -i "s|EXTRA_ARGS='|EXTRA_ARGS='--registry-mirror=加速地址 |g" /var/lib/boot2docker/profile exit docker-machine restart default 这样重启Docker后就可以用国内的镜像来加速下载了。 试一下下载一个mysql看看快不快: docker pull mysql 下载完镜像,我们运行一个容器: docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123 mysql:latest 接下来我们打开windows下的mysql客户端,服务器地址填docker虚拟机的IP地址,通过docker-machine env可以看到,我这里是192.168.99.100,然后用户名root,密码123,这样我们就可以连接到docker容器里面的mysql了。 【注意,Docker容器是在VirtualBox的虚拟机里面,不是在Windows里面,所以不能用127.0.0.1访问】

资源下载

更多资源
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应用均可从中受益。

用户登录
用户注册