spring-cloud基础
服务端发现的两种方式
- 客户端发现:Eureka
- 客户端发现:Nginx, Zookeeper, Kubernetes
服务端发现的两种方式
redis
是高性能键值对数据库,支持键值数据类型:
redis
的应用场景
session
分离redis
五种数据类型
存储 String
Value
最多可以容纳的数据长度是 512M存入键值对, 将 username:jack
存入 myhash
中
1 | hset myhash username jack |
一次存入多个键值对
1 | hmset myhash2 username rose age 21 |
根据键获取值
1 | hget myhash username |
返回
1 | "jack" |
根据多个键获取值
1 | hmget myhash2 username age |
返回
1 | 1) "rose" |
获取所有键值对
1 | hgetall myhash |
删除某个键值对
1 | hdel myhash2 username age |
返回
1 | 1) "username" |
对某个数字进行增加:将 age
属性递增5。
1 | hincrby myhash2 age 5 |
返回
1 | (integer) 26 |
rose 的 age
属性由 21
增加到 26
判断某个set中是否含有某个键
1 | hexists myhash username |
获取 hash 中的所有键
1 | hkeys myhash |
运行结果为:
1 | 1) "username" |
获取 hash 中的所有值
1 | hvals myhash |
运行结果为
1 | 1) "jack" |
往 list
中存储数据(从左侧添加,先进去的数据排在最后一位)
1 | lpush mylist a b c |
从右侧添加 (从右侧添加)
1 | rpush mylist2 a b c |
查看列表 表示从第一个到第六个元素
1 | lrange 0 5 |
运行结果如下
1 | 1) "3" |
1 | lrange mylist2 0 -1 |
表示从第一个到倒数第一个元素
运行结果如下
1 | 1) "a" |
左边弹出
1 | lpop mylist |
运行结果
1 | "3" |
右边弹出
1 | rpop mylist2 |
运行结果
1 | "3" |
lpushx
和 rpushx
仅当需要操作的 list 中有值时才进行保存操作
现在 mylist3
的数据如下
1 | 1) "5" |
移除命令 lrem
从左侧开始删除两个3
1 | lrem mylist3 2 3 |
运行后的 mylist3
结果
1 | 1) "5" |
从后边往前删除两个1
1 | lrem mylist3 -2 1 |
运行后的 mylist3
结果
1 | 1) "5" |
删除所有2
1 | lrem mylist3 0 2 |
运行后的 mylist3
结果
1 | 1) "5" |
给某个指定的角标位置的元素设置值
1 | lset mylist 3 haha |
运行结果如下
1 | 127.0.0.1:6379> lrange mylist 0 -1 |
新建一个 list
1 | 127.0.0.1:6379> lrange mylist4 0 -1 |
在第一个 b
之前插入 11
1 | 127.0.0.1:6379> LINSERT mylist4 before b 11 |
在第一个 b
之后插入 22
1 | 127.0.0.1:6379> LINSERT mylist4 after b 22 |
创建两个 list
1 | 127.0.0.1:6379> lrange mylist5 0 -1 |
现在讲 mylist5
右左边弹出,从左侧压入 mylist6
rpop
和 lpush
用于消息队列
1 | 127.0.0.1:6379> RPOPLPUSH mylist5 mylist6 |
set
类型不允许出现相同的操作,set
通常用作
创建一个 set
并添加一个元素
1 | sadd myset a |
查看所有元素发现没有重复元素
1 | 127.0.0.1:6379> SMEMBERS myset |
查看一个元素是否存在于某个集合
1 | 127.0.0.1:6379> SISMEMBER myset a |
说明 myset
中包含 a
,但不包含 x
创建两个 set
1 | 127.0.0.1:6379> SMEMBERS mya1 |
找出 mya1
中有,但是 myb1
中没有的元素
1 | 127.0.0.1:6379> sdiff mya1 myb1 |
找出 myb1
中有,但是 mya1
中没有的元素
1 | 127.0.0.1:6379> sdiff myb1 mya1 |
将 sdiff
产生的结果直接保存
1 | 127.0.0.1:6379> SDIFFSTORE my1 mya1 myb1 |
找出 mya1
,myb1
中都有的元素
1 | 127.0.0.1:6379> SINTER mya1 myb1 |
将上面的结果直接存储
1 | 127.0.0.1:6379> SINTERSTORE my2 mya1 myb1 |
列出 mya1
和 myb1
的所有元素
1 | 127.0.0.1:6379> SUNION mya1 myb1 |
将上面的结果直接保存
1 | 127.0.0.1:6379> SUNIONSTORE my3 mya1 myb1 |
查看 set
中元素的个数
1 | 127.0.0.1:6379> SCARD mya1 |
随机返回 set
中的一个元素
1 | 127.0.0.1:6379> SRANDMEMBER mya1 |
sorted set
中每一个元素都有一个分数与之关联,redis
正是通过这些分数来为 set
排序,sorted set
元素是有序的.
可以用于:
添加元素
1 | 127.0.0.1:6379> ZADD mysort 70 zs 80 ls 90 ww |
展示元素的分数
1 | 127.0.0.1:6379> ZSCORE mysort zs |
移除元素
1 | 127.0.0.1:6379> zrem mysort tom ww |
查看元素的个数
1 | 127.0.0.1:6379> ZCARD mysort |
再添加两个元素
1 | 127.0.0.1:6379> zadd mysort 85 jack 95 rose |
展示所有元素
1 | 127.0.0.1:6379> ZRANGE mysort 0 -1 |
展示所有元素和分数
1 | 127.0.0.1:6379> ZRANGE mysort 0 -1 withscores |
按分数从大到小排列
1 | 127.0.0.1:6379> ZREVRANGE mysort 0 -1 withscores |
删除排序从 0 到 3 的元素
1 | 127.0.0.1:6379> ZRANGE mysort 0 -1 |
新增几个元素
1 | 127.0.0.1:6379> zrange mysort 0 -1 withscores |
查找分数在 40 到 70 分区间的前两个元素
1 | 127.0.0.1:6379> ZRANGEBYSCORE mysort 40 70 withscores limit 0 2 |
增加分数
1 | 127.0.0.1:6379> ZINCRBY mysort 7 arrow |
查找某个分数段内元素的个数
1 | 127.0.0.1:6379> ZCOUNT mysort 50 80 |
查找所有的 keys
1 | 127.0.0.1:6379> keys * |
查看以 my
开头的所有的 key
1 | 127.0.0.1:6379> keys my? |
删除 keys
1 | 127.0.0.1:6379> del my1 my2 my3 |
判断某个 key 是否存在
1 | 127.0.0.1:6379> EXISTS hahahs |
重命名key
1 | 127.0.0.1:6379> get compnay |
设置超时时间 单位秒
1 | 127.0.0.1:6379> EXPIRE newcompany 1000 |
查看 key 的类型
1 | 127.0.0.1:6379> type newcompany |
一个 redis 实例可以包含 15 个数据库,下标是从 0-15,默认选择第 0 号数据库
切换数据库命令如下
1 | 127.0.0.1:6379> select 1 |
可以看到 1 号数据库中没有保存任何的key
将 0 号数据库中的一个 key 移动到 1 号数据库
1 | 127.0.0.1:6379> MOVE mysort 1 |
multi
相当于开启事务
开启事务后,输入的命令将被保存的队列中
exec
相当于 commit
1 | 127.0.0.1:6379> get num |
执行之后命令队列中的命令将会被全部执行
discard
相当于 rollerback
redis
之所以效率很高是因为数据都保存在内存中,为了使 redis
重启之后数据不丢失,就需要将 redis
数据保存到硬盘上。
redis
持久化有两种方式:
持久化的使用方式
redis
的快照到硬盘redis
的命令,每次重启之后读取这个文件来重现 redis
之前的状态redis
作为一个缓存服务在 redis.conf
文件中可以指定保存快照的时机
1 | save 900 1 |
AOF 方式
需要在 redis.conf
中打开配置
1 | aof 是否打开 默认是关闭的 需要改为 yes |
Spring-boot
引入 redis
缓存机制,首先需要在启动类上增加一个注解
1 |
在 controller
的方法上增加 @Cacheable
注解,一般用于查询接口
@Cacheable(cacheNames = "product", key = '#sellerId', condition="#sellerId.length>3", unless = "#result.getCode()!=0")
#sellerId
的写法成为 spel
表达式,获取参数 sellerId的值
当 sellerId
的字符长度大于 3 ,而且 除了 返回的 code!=0(也就是等于0都缓存)
当数据改变之后表用 @Cacheput
注解修改缓存,通常用于保存或者修改方法
@CachePut
要使用上面两个注解更新缓存 必须保证方法的返回值一致
如果上面两个注解的 key 不填或者为空,则key默认为方法的参数,这就会导致 key不一致的问题(即使方法的返回值一致)
可以把 cacheName
属性移到 类注解 @CacheConfig(cacheNames="product")
@CacheEvict(cacheNames = "product",key="123")
清除缓存
使用上面的缓存必须实现序列化
泛型是从 Java 5 开始增加的对于数据类型的规范和检测机制,使用泛型有如下两个优点
ClassCastException
,在编译期就可以使用泛型发现类型转换错误,或者类型不匹配请看下面的例子
1 | public class ShowExample { |
运行结果如下
1 | 这是一个String |
第一行被正确打印,但是第二行的整形却在进行强制类型转换的时候失败了,这个如果发生在程序运行期间,会给整个项目带来安全问题。所以,如果能在编译期间就发现这个问题,会对程序的健壮性有很大的提高。
泛型使用一对尖括号来设置变量的类型,多用在集合框架中。比如,如下代码定义了一个只能存放 String
类型数据的 List
。
1 | List<String> stringList = new ArrayList<String>(); |
当类中要操作的引用数据类型不确定时,就需要使用泛型。
现在我们需要定义一个工具类用来设置和获取属性值,在没有泛型之前,可以通过下面的方法实现
1 | public class GenericClassDemo { |
运行结果没有报错,因为 Object
是所有类的父类,上面的 ToolOld
通过 object
属性暂存数据,当需要获取数据时再通过强制转换,转换成原来的类型。
泛型类的定义方法如下
1 | public class ClassName<泛型标识符> { |
上面 Object
的方法可以使用泛型来代替。下面定义了一个泛型类 ToolGeneric
,每次创建对象时可以传入类型,在调用 set
方法时,如果类型不匹配,则编译不会通过。
1 | public class GenericClassDemo { |
另一个泛型类的例子
1 | public class GenericClassShowCase { |
运行结果 :
1 | 你好 |
上面的泛型类通过每次指定不同的类型,可以打印不同的数据类型。但是每打印一个新的数据类型都需要运行 PrintTool<XXX> printTool = new PrintTool<XXX>()
来创建新的类,这样也不方便,所以有必要使用泛型方法。
泛型方法定义时需要将一对尖括号包裹的泛型类型写在函数的返回值之前。
1 | public <T> void getName(T t) { |
下面是一个泛型方法的例子
1 | public class GenericMethodShow { |
1 | show: 你好 |
泛型接口的定义方式为
1 | interface IterName<T>{ |
泛型接口使用时可以在泛型类的定义时传入泛型的类型,也可以在调用方法时通过参数来确定泛型的类型
1 | public class GenericInterfaceShow { |
运行结果如下
1 | 哈哈 |
当不知道数据类型时,可以用 ?
作为通配符
1 | public class GenericExtends { |
运行结果如下
1 | a |
可以看出:使用 ?
可以匹配不同类型的数据
这个例子说明 子父类之间的泛型不能通过多态调用
1 | // 泛型限定 |
现在我们确实需要使用 printPerson
方法来打印 studentList
,将 printPerson
申明为泛型方法是一个途径:
1 | public static <T> void printPerson(ArrayList<T> personList) { |
或者使用 ?
1 | public static void printPerson(ArrayList<?> personList) { |
但是这两种方法都有局限性:无论 personList
中存储的是什么类型的数据都可以传入。这就存在了风险:就上面的例子来说,如果 personList
里边存储的不是 Person
类(或者子类),就会造成类型转换异常。为了解决这个问题,引入了类型限定。
使用 <? extends Person>
来限定 personList
只能存储 Person
类或者其子类型。
下面是一个类型限定的例子
1 | public static void printPersonExtend(ArrayList<? extends Person> personList) { |
增加一个汽车类 Car
1 | class Car { |
调用打印方法
1 | public class GenericExtends { |
与此同时,使用 <? super Student>
限定只能传入 Student
类及其父类,TreeSet
的一个构造方法就使用如下的限定符:
1 | public TreeSet(Comparator<? super E> comparator) { |
下面是一个使用 <? super E>
的例子:
定义一个 Worker
类,它同时也是 Person
的子类
1 | class Worker extends Person { |
现在我们用 TreeSet
分别存储 学生 和 工人,并将它们倒序输出
1 | public class GenericSuper { |
输出结果如下
1 | s--003 |
上面两种不同的对象比较,使用了不同的比较器,是否可以将其合并成一个呢?
查看源码 TreeSet
当前的构造函数如下
1 | public TreeSet(Comparator<? super E> comparator) { |
这个源码说明比较器参数可以传入 E
类型,或者 E
类型的 父类型
现在重写一个通用的比较器
1 | // 这里指定了类型时 Student 和 Worker 的共同父类 Person |
在定义 TreeSet
时传入上面的比较器
1 | public class GenericSuper { |
运行结果与上面相同
提示时区错误
具体报错如下
1 | The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone |
有两种解决方法
a. 在数据库路径的后面增加时区配置选项
1 | url: jdbc:mysql://localhost:3306/sell?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 |
b. 增加全局配置的选项
使用下面的语句查看当前的时区配置
1 | SHOW VARIABLES LIKE '%time_zone%' |
输出结果如下
Variable_name | Value |
---|---|
system_time_zone | |
time_zone | SYSTEM |
将时区设置为 +8
区
1 | SET GLOBAL time_zone = '+8:00' |
当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。 本地的分支并不会自动与远程仓库同步 - 你必须显式地推送想要分享的分支。 这样,你就可以把不愿意分享的内容放到私人分支上,而将需要和别人协作的内容推送到公开分支。
如果希望和别人一起在名为 serverfix 的分支上工作,你可以像推送第一个分支那样推送它。 运行 git push (remote) (branch):
1 | $ git push origin serverfix |
这里有些工作被简化了。 Git 自动将 serverfix 分支名字展开为 refs/heads/serverfix:refs/heads/serverfix,那意味着,“推送本地的 serverfix 分支来更新远程仓库上的 serverfix 分支。” 我们将会详细学习 Git 内部原理 的 refs/heads/ 部分,但是现在可以先把它放在儿。 你也可以运行 git push origin serverfix:serverfix,它会做同样的事 - 相当于它说,“推送本地的 serverfix 分支,将其作为远程仓库的 serverfix 分支” 可以通过这种格式来推送本地分支到一个命名不相同的远程分支。 如果并不想让远程仓库上的分支叫做 serverfix,可以运行 git push origin serverfix:awesomebranch 来将本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支。
下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支 origin/serverfix,指向服务器的 serverfix 分支的引用:
需要特别注意的是 可以通过 git push origin serverfix:awsomeserverfix 命令,将本地的 serverfix 分支的内容,推送到远程awsomeserverfix 分支上。
1 | $ git fetch origin |
要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的 serverfix 分支 - 只有一个不可以修改的 origin/serverfix 指针。
可以运行 git merge origin/serverfix 将这些工作合并到当前所在的分支。 如果想要在自己的 serverfix 分支上工作,可以将其建立在远程跟踪分支之上:
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch ‘serverfix’
这会给你一个用于工作的本地分支,并且起点位于 origin/serverfix。
当 git fetch
命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。 它只会获取数据然后让你自己合并。 然而,有一个命令叫作 git pull
在大多数情况下它的含义是一个 git fetch
紧接着一个 git merge
命令。 如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显式地设置还是通过 clone
或 checkout
命令为你创建的,git pull
都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然后尝试合并入那个远程分支。
由于 git pull 的魔法经常令人困惑所以通常单独显式地使用 fetch 与 merge 命令会更好一些。
1 | git push origin --delete serverfix |
删除远程仓库的 serverfix 分支
下面的命令将本地的 ccc
分支与远程仓库 origin
的 nihao
分支建立跟踪关系
1 | git branch --set-upstream-to=origin/nihao ccc |
如果不指定后面的 ccc
分支,则将当前所在分支与 origin/nihao
建立跟踪关系
项目中的文件我们经常会存储在其他的文件夹,而不是数据库,而 tomcat 服务器可以将 文件的相对路径映射为文件的绝对路径
javascript
的每个函数都包含两个非继承而来的方法:apply()
和 call()
。这两个方法都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
首先,apply() 方法接收两个参数:
this
) 的值。Array
的实例,也可以是 arguments
对象。1 | function sum(num1, num2) { |
上面的例子中, callSum1()
在执行 sum()
函数时传入了 this
作为 this
值(因为是在全局作用域中调用的,所以传入的就是 window
对象) 和 arguments
对象。而 callSum2
同样也调用了 sum()
函数,但它传入的是 this
和一个参数数组。这两个函数都会正常返回结果。
在严格模式下,未指定环境对象而调用函数,则
this
值不会转化为window
,而是undefined
。
call()
方法与 apply()
函数的作用相同,他们区别仅在于接收参数的方式不同。对于 call()
方法而言,第一个参数 this
值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用 call
方法时,传递给函数的参数必须逐个列举出来。
1 | cafunction sum(num1, num2) { |
下面是 Person
的构造函数。
1 | function Person(name, age, job) { |
上面第一种调用方法是 javascript
普通函数的调用方法,这里的 Person()
就是一个普通的 javascirpt
函数,调用过程中 this
是window
对象,这里不会返回任何的对象,最后person
的值是 undefined
。
第二种调用方法主要经历了一下 4 个步骤:
call
方法来调用,也就是 Person.call(person,'梅西', 29, 'football player' ))
。查看运行过程,我们发现 this
指向新创建的的 person
对象。
任何函数,只要通过 new
操作符来调用,那就可以当做构造函数;而任何函数,如果不通过 new
操作符来调用,那它跟普通函数也不会有什么两样
每个 javascript 函数都包含两个非继承而来的方法:apply()
和 call()
。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this
对象的值。
首先,apply()
方法接收两个参数:一个是在其中运行函数的的作用域,另一个是参数数组。其中第二个参数可以是 Array
的实例,也可以是 arguments
对象。
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function sum(num1, num2) {
return num1 + num2;
}
function callSum1(num1, num2) {
return sum.apply(this, arguments);
}
function callSum2(num1, num2) {
return sum.apply(this,[num1, num2]);
}
console.log(callSum1(10, 10));
console.log(callSum2(10, 10))
如果不小心运行了 git add *
把你不想跟踪的文件添加到了版本控制,使用如下命令撤销提交
1 | git reset HEAD <File> |
1 | git remote |
1 | git remote add <shortname> <url> |
例如
1 | $ git remote add pb https://github.com/paulboone/ticgit |
1 | git push <remote> <branch> |
下面的命令的意思是 将本地 master
分支推送到 server
服务器上.
1 | git push origin master |
1 | git remote show <remotename> |
比如
1 | $ git remote show origin |
重命名 将远程仓库由 pb 重命名 为 paul
1 | git remote rename pb paul |
移除paul
1 | git remote rm paul |
1 | git commit -a -m 'made a change' |
1 | git checkout -b hotfix |
1 | git pull origin master |
这里的 master 是指远程的 master 分支
1 | git push origin master |
将本地的 master 分支的数据,推送到它对应的 远程分支上去 这个远程分支可能是 awsomemaster
如果当前版本是 HEAD
, 那么上一个版本就是 HEAD^
,上上一个版本就是 HEAD^^
,当然往上写100个 ^
容易写不过来,所以写成 HEAD~100
。我们要将当前版本回退到上一个版本,使用如下命令:1
git reset --hard HEAD^
如果我们知道版本号,还可以使用直接指定版本号
1 | git reset --hard s7s6d734 |
使用如下命令查看命令历史
1 | git reflog |
结果如下
1 | $ git reflog |
不加参数,暂存区有文件未提交(commit)时,比较工作区和暂存区的文件
当暂存区的文件已经提交时,则比较工作区与最近一次提交的版本,如下比较名为 test1
的文件。
1 | git diff test1 |
如果同时你有一个分支叫做 test1
则以下命令
1 | git diff test1 |
则会出现如下的歧义
1 | fatal: ambiguous argument 'test1': both revision and filename |
这个时候需要制定版本号文件名
1 | git diff test1 -- test1 |
上面的命令比较当前分支工作区的 test1
文件 与 test1
分支上最新版本的 test1
文件
比较工作区与缓存区的文件
1 | git diff --cached 文件名 |
查看工作区和版本库里面最新版本的区别
1 | git diff HEAD 文件名 |
比较工作区与指定 commit-id
的区别
1 | git diff commit-id 文件名 |
比较两个 commit-id
之间的区别
1 | git diff [<>] |
查看分支
1 | git branch |
创建分支
1 | git branch <name> |
切换分支
1 | git checkout <name> |
创建+切换分支
1 | git checkout -b <name> |
删除分支
1 | git branch -d <name> |
合并分支
1 | git merge <name> |
打标签
1 | git tag <tagname> |
指定标签信息
1 | git tag -a <tagname> -m 'bulabula' |
查看所有的标签
1 | git tag |
命令git push origin
命令git push origin –tags可以推送全部未推送过的本地标签;
命令git tag -d
命令git push origin :refs/tags/