Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

第20章 广播

$
0
0

1·广播和多播要求用于UDP或原始IP,它们不能用于TCP。

2·单播IP数据报仅由通过目的IP地址指定的单个主机接收。

3·广播分组去往子网上的所以主机,包括发送主机本身。

4·(一般)除非显式告诉内核我们准备发送广播数据报,否则系统不允许我们这么做。

    const int on = 1;

    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

5·解决竞争状态:

    a.使用pselect(阻塞信号)

    b.使用sigsetjmp和siglongjmp(避免使用)

    c.使用从信号处理函数到主循环的IPC(管道等)

作者:gongluck93 发表于2017/11/16 9:46:33 原文链接
阅读:71 评论:0 查看评论

Mycat之——日志报Unknown charsetIndex:224错误

$
0
0

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/78547554

Mycat启动后,不能进行任何数据库的操作,报Unknown charsetIndex:224错误
从错误看是因为字符集问题引起的  ,因为我MYSQL服务器默认使用的是utf8mb4,所以修改mycat字符集的配置文件
#vi index_to_charset.properties
在配置文件最后加入224=utf8mb4,重启服务即正常


作者:l1028386804 发表于2017/11/16 9:51:08 原文链接
阅读:44 评论:0 查看评论

android调用dialog.hide()引起的输入事件派发错误问题追踪

$
0
0

image

问题描述:

某个界面启动后,上面的actionbar的item点击不起作用

问题调研:

00

在activity的启动过程中,创建了一个Fragment.java,在Fragment.java的createView回调中,调用了一个线程,线程中使用postUI调用dialog.show(),然后加载图片,如果没有图片,会postUi调用dialog.hide()隐藏,之后activity上面的actionbar Item点击没响应。

初步怀疑,是由于Fragment.java的写法有误,导致没有调用onCreateOptionsMenu,引起onOptionsItemSelected没有响应。但是通过断点跟踪,发现不是,这里的onCreateOptionsMenu调用了。按照网上的说法是加入setHasOptionsMenu( true );,查看代码是有此逻辑,因此可以确定,这块添加的代码是没有问题的。

于是上断点,调试DecorView.java的dispatchTouchEvent方法,为什么调试的是DecorView.java呢?因为我们activity在使用setContentView将一个布局加载起来时候,实际挂在DecorView的目录树里,因此这里便是事件的分派地方,当然,如果要说activity和inputmanager的消息传递位置,会在ViewRootImpl.java的onInputEvent方法里面。

image

我们在DecorView.java的dispatchTouchEvent方法打上断点,然后点击actionbar的item,然后发现这里的信息

image

发现这里的cb是个ProcessDialog,于是得出结论,这个当前屏幕上虽然看不到对话框(使用hide()隐藏掉),但是inputmanager那边,却还是将此事件传递给了它,所以初步结论,focus window出现错误,导致事件派发错误,引出问题。

那么,我们继续深究,从inputmanager这里,先进行一个初步判断
电脑连上手机,使用 adb shell dumpsys >~/1.txt 将dump信息存储下来,然后打开1.txt
搜索

Input Dispatcher State:

image

这里可以找到input可以传递的一个窗口列表
这里关键的几个信息:

FocusedApplication :

当前焦点app

FocusedWindow: name=’Window{f8c1e72 u0 com.codegg.fba/com.codegg.fba.activity.romListActivity}’

当前focus的窗口信息

后面紧跟着一堆窗口列表:

image

列表的一些信息:

name=

‘Window{1781b28 u0 com.codegg.fba/com.codegg.fba.activity.romListActivity}’

窗口名字,以及内存地址,title

displayId=0 

显示在哪个屏幕id上,默认为0,可以是其他,比如我们投屏到电视,或者模拟虚拟的屏幕上。

hasFocus=false

是否获取焦点

visible=true 

是否可见

canReceiveKeys=false

是否处理按键消息

layer=21025

当前在绘制里面的层大小,这个值越大,代表z序列越高,屏幕显示是按照z排序进行绘制,从低向高,如果高的layer是个全屏,则会将低值的那些界面全部覆盖。

frame=[27,780][1053,1068]

此窗口在屏幕上的布局大小

touchableRegion=[0,0][1080,1920]

此窗口的可点击区域

然后我们查找代码,去看下输入服务那边,是如何判断发送给谁的呢?

image

我们找到

InputDispatcher.cpp

findTouchedWindowAtLocked

,可以看到,这里关键的信息是:

windowInfo->visible

,由于我们排列顺序是从前往后,因此第一个遍历到对话框窗口的时候,发现

windowInfo->visible=True

,因此系统会将触摸消息,发送给这个窗口,也就是对话框。然而,实际上对话框在apk这边,已经是隐藏状态,同时自身也不消耗触摸事件,因此导致事件一直发给一个隐藏的窗口,引出问题。

01

到这里,就完了?那你还是比较年轻。虽然最终的解决方案是使用dismiss替换掉了hide,但是我们不能停留在这个表象,继续深挖下此问题。问题最终的解决,只是规避了出现此问题,但是最根本的原因,我们还需要继续寻找。

我们知道了这里有个mWindowHandles列表存储了当前的窗口,并且已经排序,那么我们找下,这个值是谁给的,因此我们在本文件查找,发现了关键方法setInputWindows,image

这里会将窗口赋值进来。然后我们全局搜索

setInputWindows

,最终在

InputMonitor.java

updateInputWindowsLw

方法里面,锁定了关键逻辑。

updateInputWindowsLw

里面,我们发现了一段很关键的代码

image

这里有个方法

isVisible = child.isVisibleLw();

会去更新显示状态,我们之前看到,就是这个变量是Ture,导致系统认为我们的对话框是可见,引出的问题。

于是我们的重心,转移到了这里,我们看下代码:

image

我们主要关心

!mAnimatingExit && !mDestroying

这两个值(

其他本身也是要关注,但是因为已经跟过,知道他们不变,所以去掉了那些无关的变量

02

当前窗口的信息,这些变量如何得知的呢?我们来看个推演过程,我们之前使用adb shell dumpsys的文档,打开,

我们通过

Input Dispatcher State

,找到了当前focus的是romListActivity,但是显示的有两个,一个是activity的主窗口,一个是对话框的窗口,对话框的layer比activity的layer高,因此它优先得到了触摸响应。
具体对话框的信息如下:

image

我们使用这里的

name=’Window{1781b28 的1781b28

,在文本中搜索,可以找到window的详细信息:

image

mHasSurface=true
mPolicyVisibility =true
mAttachedHidden=false
mAnimatingExit=false
mDestroying=false
mIsWallpaper=false
mWallpaperVisible=xxx

关于这些值怎么算出来的,是通过这里的dump信息,我们找到windowState.java的dump,我们调用的dumpsys命令,会走到这里,

image

然后这里的dump方法有这段逻辑,通过查看,我们的dumpsys里面没有出现这些数据,因此它们的值就可以确定出来的。

03

当前情况,我们是没法知晓到底是哪个值引起的问题,然后如果我们直接去看代码,分析定位到底是哪个值引起,那你会崩溃掉的,系统里面,最不喜欢跟踪的就是显示隐藏,以及动画过程,太过杂乱,很多方法频繁调用,输出的log信息过多,逻辑错综复杂,很难把握,跟进这种问题,往往太耗精力。

我这里尝试使用demo来测试,写了如下代码:

image

也就是把出问题的那段逻辑,搬出来独立测试下,发现没有问题,这样子我们就可以进行对比了。然后通过

dumpsys

之后,发现了关键数据,在dump里面,出现了一些数据:

image

我们发现,这里的mDestroying=true,所以这时的dialog.hide ()之后,窗口就不会获取焦点,同时也不是显示状态,逻辑正常。

通过对比,我们发现线索,可以追踪

mDestroying

是何时进行更新,变成true的。

我们找了很多地方,同时在每个地方,进行添加log信息,然后抓取log。同时将Windowmanage的调试信息全部打开(将

WindowManagerDebugConfig.java

里面的所有变量为false全部置成true),然后编译mmm frameworks/base/services ,make snod打包,然后将system.img刷入手机,再次进行复现问题,同时抓取log,通过查阅log,可以得出结论,
系统在修改

mDestroying

的地方,最终锁定在

WindowStateAnimator.java

的finishExit方法中。

image

这条线追到这里,那么我们就在代码查找这个finishExit里面的 这段 finishExit in 信息,想从log信息中,找到一些蛛丝马迹。

image

搜索得到一些数据,我们可以使用后面 的

WindowStateAnimator{91b6679

这里的

91b6679

便是地址,那么我们从dumpsys里面,找到当前dialog窗口的动画地址,

91b6679

image

所以我们就可以锁定到我们 dialog窗口的动画是哪个log了。

我们继续查找,使用

91b6679

,发现了一段异常逻辑。

image

这里前面可以看到,对应的窗口已经在退出window{1781b28 u0 com.codegg.fba/com.codegg.fba.activity.romListActivity

EXITING

}

log中的

addInputWindowHandle

就是系统设置input信息的地方,可以确定这里这个对话框窗口已经在退出中

image

也就是

mAnimatingExit=true

,根据之前的

isVisibleUnchecked

逻辑可知,这里如果

mAnimatingExit=true

,那么

InputMonitor.java

里面的

updateInputWindowsLw

得到的

final boolean isVisible = child.isVisibleLw();

就是false了,也就是ok的了。
通过紧跟着的log继续去看,发现了出错地方:

Update reported visibility:
Win Window{f8c1e72 

这个窗口是activity的,问题点就在这里,这里会更新,让对应的

VIS AppWindowToken{2090d

显示出来,而我们的对话框,是在这个

VIS

AppWindowToken{2090d

里面的。因为它是activity的子窗口。

于是,紧跟着的log就出现了如下语句:
OPEN TRANSACTION handleAppTransitionReadyLocked()
performing show on: WindowStateAnimator{91b6679  我们的动画重新更新了,也就不退出来。

performShow on WindowStateAnimator{91b6679

performing show on: WindowStateAnimator{9e9f896

这里是我们的activity对应的动画。

performShow on WindowStateAnimator{9e9f896

出错就在这里。然后我们需要看下这个逻辑,是怎么出现的,通过定位代码,搜索关键字

handleAppTransitionReadyLocked

找到问题点。最终我们找到,代码在

WindowSurfacePlacer.java

的 

handleOpeningApps

方法里面。

image

同时我们在

handleAppTransitionReadyLocked

方法中,看到如下语句:

image

可以看到,这时我们的标志被清除掉了,引发了问题。

然后我们在

handleOpeningApps

里面,找到一段log文字

Now opening app

,通过检索log,对比正确与错误的log备份,发现了问题。

正确的:

9886 start u0
11790 relayout dialog viewVisibility=0
12828 relayout activity viewVisibility=0
14740 WindowSurfacePlacer: ** GOOD TO GO
14883 Now opening appAppWindowToken
14946 dialog handleOpeningApps
15133 activity handleOpeningApps
15691 realyout dialog viewVisibility=8

出问题的:

3018 start u0
9023 relayout dialog viewVisibility=0
11788 relayout activity viewVisibility=0
14912 relayout dialog viewVisibility=8
19169 WindowSurfacePlacer: ** GOOD TO GO
19337 Now opening app
19403  dialog  activity handleOpeningApps

出问题的时候,这个

handleOpeningApps

的调用时机,远远晚于了

dialog.hide

的过程,因此在后续更新activity的时候,意外的将其子窗口的动画进行了重置,引发此问题。

04

这里我们再进行扩展下:我们跟踪下dialog.hide()方法,可以看到这里只是简单的修改了根节点View的显示属性。

image

那么这个属性在哪里被检测到的呢?我们知道,每个activity对应一个ViewRootImpl,系统实时都会调用这里的

image

这里performTraversals里面有个方法,叫做        final int viewVisibility = getHostVisibility();会拿到刚才hide()设置的那个View的显示隐藏状态,如果发生改变,会调用这里的

image

然后这里的relayoutWindow实质的代码位置,在:

mWindowSession.relayout 

–>mService.relayoutWindow(Session.java)

–>relayoutWindow(WindowManagerService.java)

在这个方法里面,也输出来一段关键log,这里为Relayout …: viewVisibility= 我们可以使用: viewVisibility= 去搜索log,然后使用viewVisibility=8 进行过滤,因为8=View.GONE,从而可以得出,dialog.hide()真正被系统处理的时间。错误的时候,因为触发的时机过早,导致后续的activity还没open起来,子窗口却意外的要去隐藏,导致更新时错误,引发问题。

错误的时候
01-02 16:56:39.

790 

  982  2627 V WindowManager: Relayout Window{1781b28 u0 com.codegg.fba/com.codegg.fba.activity.romListActivity}:

viewVisibility=8

然后handleOpeningApps的时间
01-02 16:56:39.

956

   982  1270 I WindowManagerService:     at com.android.server.wm.WindowSurfacePlacer.

handleOpeningApps

(WindowSurfacePlacer.java:1246)
所以是在后面,导致dialog的hide被冲掉了。

正确的时候:(demo应用)
01-02 21:13:21.

580 

  982 11320 I WindowManagerService:     at com.android.server.wm.WindowSurfacePlacer

.handleOpeningApps

(WindowSurfacePlacer.java:1246)
然后才是隐藏:
01-02 21:13:26.

939

   982  7983 V WindowManager: Relayout Window{123729 u0 wwww}:

viewVisibility=8

req=1026x483 WM.LayoutParams{(0,0)(wrapxwrap) gr=#11 sim=#120 ty=2 fl=#1820002 fmt=-3 wanim=0x1030466 surfaceInsets=Rect(96, 96 - 96, 96) needsMenuKey=2}
这个就是正确的了,系统就会判断dialog的状态是销毁中,隐藏状态,未获取焦点,输入触摸事件,则会正确的传递给对应的activity。

此问题还没追踪结束,我们继续来看log,继续细化log,再次看下问题:

正确的:

9886 start u0

11040 WindowManager: handleMessage: entry what=2

就是 REPORT_FOCUS_CHANGE = 2

11790 relayout dialog viewVisibility=0

12828 relayout activity viewVisibility=0

14127  WindowManager: handleMessage: entry what=4  

就是  DO_TRAVERSAL = 4

这个4是关键

14740 WindowSurfacePlacer: ** GOOD TO GO

14883 Now opening appAppWindowToken

14946 dialog handleOpeningApps

15133 activity handleOpeningApps

15691 realyout dialog viewVisibility=8

出问题的:

3018 start u0

6627 WindowManager: handleMessage: entry what=2

就是 REPORT_FOCUS_CHANGE = 2聚焦到dialog

9023 relayout dialog viewVisibility=0

11788 relayout activity viewVisibility=0

12595 WindowManager: handleMessage: entry what=41

14912 relayout dialog viewVisibility=8

15576 WindowManager: handleMessage: entry what=2

就是 REPORT_FOCUS_CHANGE = 2切换到acitivty

18851 WindowManager: handleMessage: entry what=4  

就是  DO_TRAVERSAL = 4这个4是关键 ,同步更新

19169 WindowSurfacePlacer: ** GOOD TO GO

wtoken.clearAnimatingFlags();

将标识在这里清掉了,导致设置的隐藏状态消失。

19337 Now opening app

19403  dialog  activity handleOpeningApps

可以看到,同步的消息必须在隐藏前被调用一次,否则便会出错。这里的同步是在WindowSurfacePlacer.java代码里面

image

于是,我们又需要去检查,出错的时候,为什么

requestTraversal

方法,触发的时机慢了一些。或者说是hide()的处理时机,为什么超前了一些呢?

错误的:

72057 22:50:44.369 start u0

73349 01-03 22:50:44.646 24013 24050 I Thread xxx: run 0—-

277ms

75853 22:50:44.947 hide dialog  

586ms

01-03 22:50:44.947 24013 24013 I Thread xxx: run 1—-

76475 relayout dialog 隐藏

77317 22:50:45.078 finishDrawingWindow

709ms

正确的:

84501 22:55:47.726  start u0

87357 22:55:47.893 24439 24439 I Thread xxx: run 0—-

167ms

96824 ViewRootImpl[wwww]: FINISHED DRAWING: wwww

96843 22:55:48.427  finishDrawingWindow: Window{b8c0aef u0 wwww}  

701ms

98403 22:55:48.520 hide dialog 794ms 01-03 22:55:48.520 24439 24439 I Thread xxx: run 1—-

794ms

98841 handleOpeningApps  dialog

99776 relayout dialog 隐藏

从时间的log来看,我们发现绘制的时间是一致的 (

finishDrawingWindow

一个

701ms

一个

709ms)

,所以就可以得出了结论,确实是线程运行的时候,这个消息抛出的时间太早,引起这里的隐藏 在系统windowstate这里处理的出现了问题,引发故障。

05

总结:挖掘此问题,主要是要解决,到底我们输入出错后,该如何分析,主要抓住dumpsys信息,看焦点窗口到底在哪个上面,然后再去根据

handleOpeningApps

viewVisibility=  
finishExit in  
handleAppTransitionReadyLocked 

等一些关键log,去推断出逻辑,同时根据代码,去排查,最终锁定问题。

最终我们抽离出来错误代码:

image

这里差异就是,使用

MainActivity.this.runOnUiThread

和使用

view.post

的微小差别。
我们看下对应代码:

MainActivity.this.runOnUiThread

image

可以看到

Activity.runOnUiThread

里面,如果不在主线程,直接给主线程post一个消息action。
如果是在主线程,直接运行。我们这里不在主线程,是给主线程post了一个消息。

image

View.post

里面,可以看到如果attachInfo为空,就扔到一个队列里面,后续在

dispatchAttachedToWindow

回调中才取出来,所以就会将消息向后推迟一会,就是这一会,状态就OK的啦。

技术在于灵活使用,才能发挥巨大作用。

本文完。喜欢本文,分享给别人,喜欢代码GG,扫二维码,关注代码GG之家。

image

作者:a332324956 发表于2017/11/16 9:57:21 原文链接
阅读:52 评论:0 查看评论

【比特币】 BIP-0037 详细说明

$
0
0

BIP-0037 详细说明

  BIP: 37
  Layer: Peer Services
  Title: 连接布隆过滤器
  Author: Mike Hearn <hearn@google.com>
          Matt Corallo <bip37@bluematt.me>
  Comments-Summary: No comments yet.
  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0037
  Status: Final
  Type: Standards Track
  Created: 2012-10-24
  License: PD

概述

这个BIP为P2P协议增加了新的支持,允许同行减少他们发送的交易数据量。 在版本握手完成之后,节点可以选择在每个连接上设置过滤器。过滤器被定义为从交易派生的数据的布隆过滤器。 布隆过滤器是一个概率数据结构,允许测试集成员 - 他们可以有误报,但不是假的否定。

本文不会详细介绍布隆过滤器如何工作,读者可以参考维基百科进行介绍。

动机

随着比特币使用量的增长,下载数据块和交易广播所需的带宽量也随之增加。 实施简化支付验证的客户不会试图完全验证区块链,而只是检查区块标题连接在一起是正确的,并且相信高难度链中的交易实际上是有效的。 有关此模式的更多详细信息,请参阅比特币纸张。

今天,SPV客户端必须下载块和全部广播交易的全部内容,才能抛弃绝大多数与钱包无关的交易。 这减慢了他们的同步过程,浪费了用户带宽(在手机上经常被测量)并且增加了内存使用。 所有这三个问题都触发了真正的用户投诉Android“比特币钱包”应用程序,实施SPV模式。 为了使链同步更快,更便宜,并且能够在内存有限的旧电话上运行,我们希望远程对等方在通过网络发送不相关的事务之前丢弃它们。

设计原理

实现既定目标的最明显的方法是让客户端将密钥列表上传到远程节点。 出于以下原因,我们采取更复杂的方法:

  • 隐私:由于Bloom过滤器是概率性的,客户端选择误报率,所以节点可以在精度和带宽使用之间进行权衡。 一个能够获得大量带宽的节点可能会选择高FP率,这意味着远程节点无法准确地知道哪些事务属于客户端,哪些不属于客户端。 一个带宽非常小的节点可能会选择使用非常准确的过滤器,这意味着它们只能获得与其钱包实际相关的已发送事务,而远程对等可能能够将事务与IP地址(以及相互之间)相关联。
  • 布隆过滤器是紧凑的,测试它们的成员资格是快速的。 这样就可以以最小的风险开启DoS攻击的潜力,从而达到令人满意的性能特点。

规格

新消息

我们开始在协议中添加三条新消息:

  • filterload 其中设置当前布隆过滤器在连接上
  • filteradd 它将给定的数据元素添加到连接当前过滤器,而不需要设置全新的数据元素
  • filterclear 这会删除当前的过滤器并返回到BIP37的常规使用。

请注意,不存在filterremove命令,因为从本质上来说,Bloom过滤器是仅附加数据结构。 一旦添加了一个元素,就不能从头开始重建整个结构。

filterload命令定义如下:

字段大小 描述 数据类型 注释
? filter uint8_t[] 过滤器本身只是一个任意字节对齐大小的位字段。 最大大小是36,000字节。
4 nHashFuncs uint32_t 在此过滤器中使用的散列函数的数量。 该字段中允许的最大值是50。
4 nTweak uint32_t 将随机值添加到布隆过滤器所使用的哈希函数中的种子值。
1 nFlags uint8_t 一组控制如何将匹配项添加到过滤器的标志。

请参阅下面有关Bloom过滤器算法的说明,以及如何选择nHashFuncs和过滤器大小以获得所需的误报率。

在接收到一个filterload命令后,远程节点将立即将其宣告的广播事务(在inv包中)限制为匹配过滤器的事务,其中匹配算法在下面指定。 这些标志控制匹配算法的更新行为。

filteradd命令定义如下:

字段大小 描述 数据类型 注释
? data uint8_t[] 要添加到当前过滤器的数据元素。

数据字段的大小必须小于或等于520字节(任何可能匹配的对象的最大大小)。

给定的数据元素将被添加到布隆过滤器。 必须先使用filterload提供过滤器。 这个命令是有用的,如果一个新的密钥或脚本被添加到一个客户端钱包,同时它的网络连接打开,它避免了重新计算和发送一个全新的过滤器到每个节点的需要(虽然这样做通常建议 保持匿名)。

filterclear命令完全没有参数。

在设置过滤器后,节点不仅停止通告不匹配的交易,还可以为过滤的块提供服务。 一个过滤的块由merkleblock消息定义,并且定义如下:

字段大小 描述 数据类型 注释
4 version uint32_t 块的版本信息,根据软件版本创建此块
32 prev_block char[32] 先前块的特定块的引用的哈希值
32 merkle_root char[32] 对Merkle树集合的引用,它是与此块相关的所有事务的散列
4 timestamp uint32_t 创建此块时的时间戳记录(限于2106!)
4 bits uint32_t 计算的难度目标被用于这个块
4 nonce uint32_t 用于生成此块…随机数,以允许头的变化,并计算不同的散列
4 total_transactions uint32_t 块中的交易数量(包括不匹配的)
hashes uint256[] 散列深度优先(包括标准的varint大小前缀)
flags byte[] 标志位,每8个字节打包,最低有效位先(包括标准的varint大小前缀)

请参阅下面的部分merkle树散列和标志的格式。

因此,merkleblock消息是一个块头,加上一个merkle树的一部分,可以用来提取匹配过滤器的事务的标识信息,并证明匹配的事务数据确实出现在解决的块中。 客户可以使用这些数据来确保远程节点没有给他们提供从未出现在实际块中的假交易,虽然说谎通过遗漏仍然是可能的。

扩展到现有消息

版本命令扩展了一个新的字段:

字段大小 描述 数据类型 注释
1 byte fRelay bool 如果为false,则在收到过滤器{load,add,clear}命令之前,不会公布广播事务。 如果丢失或者为真,协议行为不会发生变化。

希望使用Bloom过滤的SPV客户端通常会在版本消息中将fRelay设置为false,然后根据自己的钱包(或其子集,如果它们重叠不同的对等方)设置过滤器。 在设置过滤器之前,能够选择退出inv消息可以防止客户端在完成版本握手和设置过滤器之间的短暂时间窗口中充斥流量。

getdata命令被扩展为允许inv子消息中的新类型。类型字段现在可以是MSG_FILTERED_BLOCK(== 3)而不是MSG_BLOCK。如果连接上没有设置过滤器,则忽略过滤块的请求。 如果已设置过滤器,则会为请求的块散列返回一个merkleblock消息。此外,由于merkleblock消息仅包含交易散列列表,因此匹配过滤器的交易也应在发送merkleblock后的单独tx消息中发送。 这避免了本来需要的慢速往返(接收哈希,没有看到一些这些事务,请求)。 请注意,由于目前没有办法请求节点中已经在块中的事务(除了请求完整块)外,请求节点还没有用inv发送或通告的一组匹配事务必须是 发送并且任何与该过滤器相匹配的附加交易也可以被发送。 这允许客户端(例如参考客户端)限制其必须记住给定节点的inv数量,同时仍然向节点提供所需的所有事务。

过滤器匹配算法

筛选器可以针对任意数据进行测试,以查看客户端是否插入了该数据。 因此,出现了什么样的数据应该被插入/测试的问题。

要确定事务是否与过滤器匹配,使用以下算法。 一旦找到匹配,算法就会中止。

  1. 测试交易本身的散列。
  2. 对于每个输出,测试输出脚本的每个数据元素。 这意味着输出脚本中的每个散列和键都是独立测试的。 重要提示:如果在测试交易时输出匹配,则节点可能需要通过插入序列化的COutPoint结构来更新过滤器。 请参阅下面的更多细节。
  3. 对于每个输入,测试序列化的COutPoint结构。
  4. 对于每个输入,测试输入脚本的每个数据元素(注意:输入脚本只包含数据元素)。
  5. 否则没有匹配。

通过这种方式,地址,键和脚本散列(对于P2SH输出)都可以被添加到过滤器中。 您还可以匹配在输入或输出中标记有众所周知的数据元素的事务类,例如,实现各种形式的智能属性。

为了确保你可以在你的钱包中找到交易消费的输出,即使你对他们的形式一无所知。 正如你所看到的,一旦设置了连接,过滤器就不是静态的,并且可以在整个连接生命周期中改变。 这样做是为了避免以下竞争条件:

  1. 客户端设置一个与钱包中的密钥匹配的过滤器。 然后他们开始下载块链。 使用getblocks来请求客户端丢失的链的部分。
  2. 服务节点从磁盘读取第一个块。 它包含发送钱给客户端密钥的TX 1。 它匹配过滤器,因此被发送到客户端。
  3. 服务对端从磁盘读取第二个数据块。 它包含发送TX 1的TX 2,但TX 2不包含任何客户密钥,因此不发送。 客户不知道他们收到的钱已经用完了。

通过在步骤2中利用所发现的端点原子地更新布隆过滤器,在步骤3中过滤器将与TX 2匹配,并且尽管在处理第一和第二块的节点之间没有暂停,客户端将会了解所有相关交易。

过滤器的nFlags字段控制节点的精确更新行为,并且是一个位字段。

  • BLOOM_UPDATE_NONE(0)表示找到匹配项时不调整过滤器。
  • BLOOM_UPDATE_ALL(1)表示如果过滤器匹配scriptPubKey中的任何数据元素,则将outpoint序列化并插入到过滤器中。
  • BLOOM_UPDATE_P2PUBKEY_ONLY(1)表示只有匹配scriptPubKey中的数据元素,并且该脚本具有标准的“pay to pubkey”或“pay to multisig”形式,才会将outpoint插入到过滤器中。

这些区别有助于避免由于错误的阳性率增加而使滤波器过快劣化。 我们可以观察到,一个预计只接收标准付款地址表单的钱包的钱包不需要自动过滤器更新,因为任何花费其自己的输出的交易在输入中具有可预测的数据元素(公钥 哈希到地址)。如果钱包可能收到付费地址输出以及付费到付费或付费多输出,那么BLOOM_UPDATE_P2PUBKEY_ONLY是合适的,因为它避免了最常见输出类型的过滤器的不必要的扩展,但仍能确保正确的行为 付款明确指定密钥。

显然,nFlags == 1或nFlags == 2意味着随着更多的链扫描,过滤器会变得更脏。 客户应该监测观察到的假阳性率,并定期用干净的刷新过滤器。

部分Merkle分支格式

Merkle树是一种将一组项目排列为树的叶节点的方式,其中内部节点是其子哈希连接的哈希(hash)。 根节点被称为Merkle根。每个比特币块都包含从块交易形成的树的Merkle根。 通过提供树内部节点的一些元素(称为Merkle分支),形成了一个证明,即在开采时给定的事务确实在块中,但是证明的大小远小于原始块的大小。

构建一个部分Merkle树对象

  • 遍历从根向下的merkle树,并为每个遇到的节点:
    • 检查此节点是否对应于要包含的叶子节点(事务)或其父节点:
    • 如果是这样,附加一个“1”比特的标志位
    • 否则,追加一个“0”比特位
    • 检查此节点是否为内部节点(非叶节点)并且 是包含的叶节点的父节点:
    • 如果是这样的话:
    • 下降到其左侧子节点,并完全处理其下的子树(深度优先)。
    • 如果这个节点也有一个正确的子节点,那么也进入它。
    • 否则:将此节点的哈希附加到哈希列表。

解析部分Merkle树对象

由于部分块消息包含整个块中的事务数量,所以先前已知该Merkle树的形状。 再次,遍历这棵树,计算遍历节点的散列:

  • 从标志位列表中读一个比特位
    • 如果是’0’
    • 从哈希列表中读取哈希值,并将其作为该节点的哈希值返回。
    • 如果它是“1”并且这是叶节点:
    • 下降到其左侧的子树,并将其计算的散列存储为L.
    • 如果这个节点也有一个正确的子节点:
    • 下降到其正确的子节点,并将其计算的散列存储为R.
    • 如果L == R,则部分Merkle树对象无效。
    • 返回 Hash(L || R).
    • 如果此节点没有正确的子节点,则返回Hash(L || L)。

部分merkle树对象仅在以下情况下有效:

  • 哈希列表中的所有哈希都被消耗掉了。
  • 在标志位列表中的所有比特位被消耗(除了填充,使之成为一个完整的字节),并没有更多的。
  • 为根节点计算的散列匹配区块头部的merkle根。
  • 区块头部是有效的,其声称工作量证明相匹配。
  • 在两个孩子的节点中,左右分支的哈希值永远不相等。

布隆过滤器格式

布隆过滤器是一个位域,其中根据将数据元素馈送到一组不同的散列函数来设置位。 所使用的散列函数的数量是过滤器的参数。 在比特币中,我们使用32位Murmur哈希函数的版本3。 为了得到N个“不同的”散列函数,我们只需用下面的公式初始化Murmur算法:

nHashNum * 0xFBA4C795 + nTweak

即如果过滤器用4个散列函数初始化并且调整为0x00000005,则当需要第二函数(索引1)时,h1将等于4221880218。

使用 filterload命令加载过滤器时,可以选择两个参数。 一个是以字节为单位的过滤器的大小。 另一个是要使用的散列函数的数量。 要选择参数,您可以使用以下公式:

假设N是你想插入到集合中的元素的数量,P是误报的概率,其中1.0是“匹配所有”,零是无法实现的。

以字节为单位的过滤器的大小S由(-1 / pow(log(2), 2) * N * log(P)) / 8给出。当然,您必须确保它不超过最大大小(36,000 :因为它代表20,000个项目的过滤器,误报率<0.1%或10,000个项目,误报率<0.0001%)。

所需散列函数的数量由S * 8 / N * log(2)给出。

版权

该文件被放置在公共领域。

参考资料

https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki

https://zh.wikipedia.org/wiki/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8

https://en.wikipedia.org/wiki/Bloom_filter

https://en.bitcoin.it/wiki/Smart_Property

https://github.com/cpselvis/zhihu-crawler/wiki/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E5%AE%9E%E7%8E%B0

作者:diandianxiyu 发表于2017/11/16 10:21:20 原文链接
阅读:108 评论:0 查看评论

LeetCode--Recover Binary Search Tree

$
0
0

Two elements of a binary search tree (BST) are swapped by mistake.

Recover the tree without changing its structure.

Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?

思路:中序遍历。
这道题O(n)的思路就是直接中序遍历,然后开一个数组存放所有的元素,找到交换位置的两个元素,最后重新遍历,把两个元素交换。
常数空间的思路就是一次中序遍历,用三个指针,s1,s2,pre分别记录要交换的两个指针和root前一个指针,然后注意第一次找到的一定是要交换的s1指针,但是可能第二次找到的不是最终要交换的s2指针,还需要继续遍历,更新pre和root,找到最后需要交换的s2指针。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* s1,*s2,*pre;
    void dfs(TreeNode* root){
        if(!root) return;
        dfs(root->left);
        if(pre&&pre->val>root->val){
            if(s1==NULL){
                s1=pre;
                s2=root;
            }
            else
                s2=root;
        }
        pre=root;
        dfs(root->right);
    }
    void recoverTree(TreeNode* root) {
        if(!root) return;
        s1=s2=pre=NULL;
        dfs(root);
        swap(s1->val,s2->val);
    }
};
作者:qq_20791919 发表于2017/11/16 10:28:35 原文链接
阅读:66 评论:0 查看评论

LeetCode-105:Construct Binary Tree from Preorder and Inorder Traversal (利用先序和中序遍历构建二叉树) -- medium

$
0
0

Question

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:

  • You may assume that duplicates do not exist in the tree.

问题解析:

给定一棵树的先序和中序遍历数组,构建该二叉树。

Answer

Solution 1:

数据结构,递归调用。

LeetCode-106:Construct Binary Tree from Inorder and Postorder Traversal的思想是相同的。

  • 利用先序遍历的最先的一个元素为子树的根结点,该根结点在中序遍历的位置为左右子树的分割点。
  • 需要注意左右子树的边界。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder == null || inorder == null || preorder.length != inorder.length) return null;
        Map<Integer, Integer> inMap = new HashMap<Integer, Integer>();
        for(int i = 0; i < inorder.length; i++) {
            inMap.put(inorder[i], i);
        }

        TreeNode root = buildTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1, inMap);
        return root;
    }

    public TreeNode buildTree(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd, Map<Integer, Integer> inMap) {
        if(preStart > preEnd || inStart > inEnd) return null;

        TreeNode root = new TreeNode(preorder[preStart]);
        int inRoot = inMap.get(root.val);
        int numsLeft = inRoot - inStart;

        root.left = buildTree(preorder, preStart + 1, preStart + numsLeft, inorder, inStart, inRoot - 1, inMap);
        root.right = buildTree(preorder, preStart + numsLeft + 1, preEnd, inorder, inRoot + 1, inEnd, inMap);

        return root;
    }
}
  • 时间复杂度:O(lgn),空间复杂度:O(n)

Solution 2:

python 解法,更加简洁。

  • 以preorder从前到后一直保存的是子树的根结点,所以取根结点直接pop(0),先建立左子树,再建立右子树。
  • 更加清晰简洁。
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if not preorder or not inorder:
            return None

        root = TreeNode(preorder.pop(0))
        preorderindex = inorder.index(root.val)

        root.left = self.buildTree(preorder, inorder[:preorderindex])
        root.right = self.buildTree(preorder, inorder[preorderindex+1:])

        return root
作者:Koala_Tree 发表于2017/11/16 10:52:00 原文链接
阅读:32 评论:0 查看评论

TensorFlow版本带来的concat错误

$
0
0

错误提示:

TypeError: Expected int32, got list containing Tensors of type ‘_Message’ instead.

错误说明:

根据提示知道代码中一行concat相关的代码。
是由于TensorFlow版本带来的错误。

在TensorFlow 1.0以前的版本(0.x)的API中,concat的参数是数字在前,tensors在后的:

tf.concat(3, net, name=name)

而在TensorFlow 1.0版本以后的API中,concat的参数是tensors在前,数字在后的:

tf.concat(net, 3, name=name)

因为参考的代码可能当时运行的TensorFlow版本与本机版本不同,所以有了问题。

解决方案:

根据错误提示找到对应代码行,把concat的参数调换一下顺序就可以成功运行了。


版权所有:http://blog.csdn.net/cloudox_
参考:http://blog.csdn.net/zcf1784266476/article/details/71248799

作者:Cloudox_ 发表于2017/11/16 11:05:35 原文链接
阅读:14 评论:0 查看评论

TensorFlow的几点小知识

$
0
0

1、调节GPU占比

TensorFlow比较贪心,默认会占用全部的GPU的资源。可以通过以下方式调节:

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.9
session = tf.Session(config=config)

另外,可以按需分配GPU资源

config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config)

2、寻找网络中的变量

在训练网络的过程中,可能只需要训练部分参数,固定其他参数,那怎么用TF实现呢?

我们可以在定义参数的时候采用

with tf.variable_scope('var'): 
这一结构,相当于这些变量都是在scope 'var'下面。然后使用
theta = tf.get_collection(tf.GraphKeys.TRAINING_VARIABLES, scope='var')
就可以提取这些变量,在训练的时候,指定这些变量作为训练变量即可。

optimizer = ly.optimize_loss(loss=your_loss, learning_rate=your_learning_rate, optimizer=tf.train.AdamOptimizer, variables=theta, ...)


3、权值共享

我们可以使用

with tf.variable_scope('var'): 
来实现这一功能。假设A,B,C权值共享,先定义A的网络结构和scope,然后定义B,C相同的网络结构,使用和A相同的scope,同时将reuse设置为True

with tf.variable_scope('var', reuse=True): 

4、多GPU中,指定GPU运行TF

CUDA_VISIBLE_DEVICES=0 python -i train.py


未完待续。。。




作者:helei001 发表于2017/11/16 11:24:11 原文链接
阅读:52 评论:0 查看评论

Unity Shader 学习笔记(10) 纹理(Texture)的属性

$
0
0

Unity Shader 学习笔记(10) 纹理(Texture)的属性

参考书籍:《Unity Shader 入门精要》
官网API:Textures
详解Unity3d游戏开发中Texture贴图纹理及相关属性

*版本:2017.1.1f1


Texture Type

选择适合的类型,Unity会为Unity Shader传递正确的纹理,并对一些纹理进行优化。

Defualt(默认纹理)

Normal map(法线纹理)

  使用了法线纹理类型,Unity会根据不同平台对纹理进行压缩,如DXT5nm格式,通过UnpackNormal函数进行采样,见下面UnityCG.cginc中源码。这种压缩只用两个通道(原来三个),减少法线纹理占用的内存空间。


inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{
    fixed3 normal;
    normal.xy = packednormal.wy * 2 - 1;
    normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
    return normal;
}

inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
    return packednormal.xyz * 2 - 1;
#else
    return UnpackNormalDXT5nm(packednormal);
#endif
}

Create from Grayscale

用于高度图生成法线纹理。这样就可以和切线空间下的法线纹理同等对待了。
- Bumpiness:控制凹凸程度
- Filtering:决定凹凸程度计算方式
- Smooth:生成平滑的法线纹理
- Sharp:使用Sobel滤波(一种边缘检测使用的滤波器)生成法线纹理


WrapMode

决定纹理坐标超过[0, 1]范围后将会被如何平铺(Tiling)。

  • Repeat:如果纹理坐标超过1(同理小于0)时,整数部分会被舍弃,如值为2.23采样值等于0.23。
  • Clamp:如果纹理坐标超过1(同理小于0)时,会截取到1(或0)。

渐变纹理应该选择Clamp(下右图)。因为Repeat(下左图)可能产生如下错误,大于1的值如1.01会采样为0.01,为黑色。


FilterMode

决定纹理拉伸时采样哪种滤波模式。

Point,Bilinear,Trilinear对比如下,效果依次提升,性能消耗同样增大。

  • Point:最近邻滤波,缩放是采样像素数目只有一个。
  • Bilinear:使用线性滤波,找到四个近邻像素,然后进行线性插值混合得到,图像模糊了。
  • Trilinear:几乎和Bilinear一样,只是如果开启多级渐远纹理技术,那就会在多级渐远纹理之间进行混合。

多级渐远纹理(mipmapping)

面板中Advanced->Generate Mip Maps默认开启。原理是提前用滤波处理得到更小的图形,每一层都是上一层图像降采样得到,以此类推,形成一个图像金字塔。在物体远离摄像机时,直接使用较小的纹理。缺点是通常会多占用33%的内存空间。


作者:l773575310 发表于2017/11/16 11:29:04 原文链接
阅读:22 评论:0 查看评论

非对称加密技术- RSA算法数学原理分析

$
0
0

非对称加密技术,在现在网络中,有非常广泛应用。加密技术更是数字货币的基础。

所谓非对称,就是指该算法需要一对密钥,使用其中一个(公钥)加密,则需要用另一个(私钥)才能解密。
但是对于其原理大部分同学应该都是一知半解,今天就来分析下经典的非对称加密算法 - RSA算法。
通过本文的分析,可以更好的理解非对称加密原理,可以让我们更好的使用非对称加密技术。

题外话:
并博客一直有打算写一系列文章通俗的密码学,昨天给站点上https, 因其中使用了RSA算法,就查了一下,发现现在网上介绍RSA算法的文章都写的太难理解了,反正也准备写密码学,就先写RSA算法吧,下面开始正文。

RSA算法原理

RSA算法的基于这样的数学事实:两个大质数相乘得到的大数难以被因式分解。
如:有很大质数p跟q,很容易算出N,使得 N = p * q,
但给出N, 比较难找p q(没有很好的方式, 只有不停的尝试)

这其实也是单向函数的概念

下面来看看数学演算过程

  1. 选取两个大质数p,q,计算N = p * q 及 φ ( N ) = φ (p) * φ (q) = (p-1) * (q-1)

    三个数学概念:
    质数(prime numbe):又称素数,为在大于1的自然数中,除了1和它本身以外不再有其他因数。
    互质关系:如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系(coprime)。
    φ(N):叫做欧拉函数,是指任意给定正整数N,在小于等于N的正整数之中,有多少个与N构成互质关系。

    如果n是质数,则 φ(n)=n-1。
    如果n可以分解成两个互质的整数之积, φ(n) = φ(p1p2) = φ(p1)φ(p2)。即积的欧拉函数等于各个因子的欧拉函数之积。

  2. 选择一个大于1 小于φ(N)的数e,使得 e 和 φ(N)互质

    e其实是1和φ(N)之前的一个质数

  3. 计算d,使得d*e=1 mod φ(N) 等价于方程式 ed-1 = k * φ(N) 求一组解。

    d 称为e的模反元素,e 和 φ(N)互质就肯定存在d。

    模反元素是指如果两个正整数a和n互质,那么一定可以找到整数b,使得ab被n除的余数是1,则b称为a的模反元素。
    可根据欧拉定理证明模反元素存在,欧拉定理是指若n,a互质,则:
    a^φ(n) ≡ 1(mod n) 及 a^φ(n) = a * a^(φ(n) - 1), 可得a的 φ(n)-1 次方,就是a的模反元素。

  4. (N, e)封装成公钥,(N, d)封装成私钥。
    假设m为明文,加密就是算出密文c:
    m^e mod N = c (明文m用公钥e加密并和随机数N取余得到密文c)
    解密则是:
    c^d mod N = m (密文c用密钥解密并和随机数N取余得到明文m)

    私钥解密这个是可以证明的,这里不展开了。

加解密步骤

具体还是来看看步骤,举个例子,假设Alice和Bob又要相互通信。
1. Alice 随机取大质数P1=53,P2=59,那N=53*59=3127,φ(N)=3016
2. 取一个e=3,计算出d=2011。
3. 只将N=3127,e=3 作为公钥传给Bob(公钥公开)
4. 假设Bob需要加密的明文m=89,c = 89^3 mod 3127=1394,于是Bob传回c=1394。 (公钥加密过程)
5. Alice使用c^d mod N = 1394^2011 mod 3127,就能得到明文m=89。 (私钥解密过程)

假如攻击者能截取到公钥n=3127,e=3及密文c=1394,是仍然无法不通过d来进行密文解密的。

安全性分析

那么,有无可能在已知n和e的情况下,推导出d?

  1. ed≡1 (mod φ(n))。只有知道e和φ(n),才能算出d。
  2. φ(n)=(p-1)(q-1)。只有知道p和q,才能算出φ(n)。
  3. n=pq。只有将n因数分解,才能算出p和q。

如果n可以被因数分解,d就可以算出,因此RSA安全性建立在N的因式分解上。大整数的因数分解,是一件非常困难的事情。
只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。

补充模运算规则

  1. 模运算加减法:
    (a + b) mod p = (a mod p + b mod p) mod p
    (a - b) mod p = (a mod p - b mod p) mod p
  2. 模运算乘法:
    (a * b) mod p = (a mod p * b mod p) mod p
  3. 模运算幂
    a ^ b mod p = ((a mod p)^b) mod p

深入浅出区块链 - 系统学习区块链,打造最好的区块链技术博客

作者:xilibi2003 发表于2017/11/16 9:59:08 原文链接
阅读:1 评论:0 查看评论

poj2109 Power of Cryptography【坑~泪目】【水过~】

$
0
0

Power of Cryptography
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 26249   Accepted: 13121

Description

Current work in cryptography involves (among other things) large prime numbers and computing powers of numbers among these primes. Work in this area has resulted in the practical use of results from number theory and other branches of mathematics once considered to be only of theoretical interest. 
This problem involves the efficient computation of integer roots of numbers. 
Given an integer n>=1 and an integer p>= 1 you have to write a program that determines the n th positive root of p. In this problem, given such integers n and p, p will always be of the form k to the nth. power, for an integer k (this integer is what your program must find).

Input

The input consists of a sequence of integer pairs n and p with each integer on a line by itself. For all such pairs 1<=n<= 200, 1<=p<10101 and there exists an integer k, 1<=k<=109 such that kn = p.

Output

For each integer pair n and p the value k should be printed, i.e., the number k such that k n =p.

Sample Input

2 16
3 27
7 4357186184021382204544

Sample Output

4
3
1234

Source

题意:输入n,p找到满足k^n = p的k。

数据范围给的这么大,吓死我了,以前没有写过这种类型的题,一点思路都没有,没忍住找了一下题解,发现double都能过哎。

然后,老师说,我这么水过的不算,自己出数据然后自己写标程,啊!!!!

#include<stdio.h>
#include<math.h>

int main()
{
    double n,p;
    while(scanf("%lf%lf",&n,&p)!=EOF)
    {
        printf("%.0lf\n",pow(p,1/n));
    }
    return 0;
}


作者:hello_sheep 发表于2017/11/16 13:20:59 原文链接
阅读:14 评论:0 查看评论

elasticsearch详解(一)——es是什么、能做什么?

$
0
0

es是什么

elasticsearch简写es,es是一个高扩展、开源的全文检索和分析引擎,它可以准实时地快速存储、搜索、分析海量的数据。

什么是全文检索

全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。全文搜索搜索引擎数据库中的数据。

es的应用场景

  • 一个线上商城系统,用户需要搜索商城上的商品。
    在这里你可以用es存储所有的商品信息和库存信息,用户只需要输入”空调”就可以搜索到他需要搜索到的商品。
  • 一个运行的系统需要收集日志,用这些日志来分析、挖掘从而获取系统业务未来的趋势。
    你可以用logstash(elk中的一个产品,elasticsearch/logstash/kibana)收集、转换你的日志,并将他们存储到es中。一旦数据到达es中,就你可以在里面搜索、运行聚合函数等操作来挖掘任何你感兴趣的信息。
  • 如果你有想基于大量数据(数百万甚至数十亿的数据)快速调查、分析并且要将分析结果可视化的需求。
    你可以用es来存储你的数据,用kibana构建自定义的可视化图形、报表,为业务决策提供科学的数据依据。

直白点讲,es是一个企业级海量数据的搜索引擎,可以理解为是一个企业级的百度搜索,除了搜索之外,es还可以快速的实现聚合运算。

作者:Dante_003 发表于2017/11/16 14:35:28 原文链接
阅读:14 评论:0 查看评论

elasticsearch详解(二)——初识es

$
0
0

基本概念

es里面有很多核心概念,从一开始了解这些概念会更有助于了解es。

准实时(NRT,near realtime)

es是一个准实时的搜索平台,这就意味当你存一条数据进去到可以搜索到中间有一定延迟(一般是一秒左右)。

集群

集群是存有数据多个节点的集合。集群用名字来作为唯一标志,默认为“elasticsearch”。集群的名字非常重要,因为一个节点只能属于一个集群。

节点

es集群的一部分,节点是用来存储数据、提供搜索请求计算的单位。es节点和es集群一样都是用名字来作为唯一标识的,es节点默认的名字是uuid。

es节点在一个网段里面是有自动发现功能的,节点启动前配置好节点要加入的集群名称,es节点就会自动加入集群。

在一个集群里面,你可以加入任意多你想添加的节点。

下面的概念不翻译成中文了,因为会和传统数据库的叫法混淆。

index

索引。索引是一组具有相同特点文档的集合。在es里面,索引是参与搜索、更新、删除的基本单位。
在一个集群里面,你可以创建任意多的索引。
在这里的索引不同于传统数据库的索引。

type

类型。在索引里面,你可以定义多个类型,类型在索引里面仅仅是一个逻辑层面的分类字段。在索引里面,你可以将里面的所有字段按照分类从逻辑层面用类型来区分。

document

文档。文档在es里面是数据存储的最基本单元。

shard&relicas(分片和副本)

es是一个分布式的系统,分布式就必须要考虑到容灾、数据丢失的容错问题。

shard(分片)

一个index是可以存储超过一台节点硬件极限的数据。为了能够让更多的数据存储在es集群里面,es能够让index打散成多个分片分布在不同的节点上从而增大index存储数据的量。这个打散的碎片就是shard分片。

每个index创建的时候都能定义分片的数量,定义好后是不能修改的。

  • 分片可以水平扩展index存储的数据量
  • 分片可以让计算请求分为多个并行处理。一个分片会有一个线程处理一份数据,多个分片会有多个线程来处理一个请求。

replication(副本)

分布式系统都会有副本的概念,因为数据分散的分布在不同的机器上,难免会遇到网络、死机、硬盘损坏等造成数据损坏、丢失等严重情况,所以一份数据会有多个副本保证数据的完整安全性。

这里副本实际上是分片的副本,数据在节点上以分片数据的形式存在,es通过算法使每台机器上的碎片副本保存在其它机器上,保证在down掉更多机器的情况下依然保证数据不会丢失。

  • 当分片或者几点down掉之后,副本依然能够保证数据的完整性,所以分片的副本绝不会和原始分片分布在一台机器上。
  • 因为搜索的时候,也会并行的在分片副本上进行,所以副本会扩大你集群搜索的吞吐量,从而加快搜索的速度

创建index的时候可以定义分片和副本的数量,副本的数量可以动态修改,分片的数量一旦定义就不能修改。
总而言之,言而总之,副本和分片数量都对index的搜索速度有影响。

分片越多,搜索的线程越多,占用的资源多,自然快,适用于数据量很大的情况。数据量很小分片很多的时候会造成资源的浪费。

副本越多,可以搜索的文件越多,会提升搜索的速度,但是副本多也会对写入速度造成影响。

所以要根据数据实际使用的场景合理设置副本和分片的数量。

作者:Dante_003 发表于2017/11/16 14:39:07 原文链接
阅读:12 评论:0 查看评论

数据结构与算法分析(Java语言描述)(21)—— 并查集基础

$
0
0

这里写图片描述

package com.dataStructure.union_find;

public class UnionFind1 {
    // 第一版的 Union-Find 本质是一个数组
    private int[] id;
    // 数据个数
    private int count;

    public UnionFind1(int n) {
        count = n;
        id = new int[n];
        // 初始化,使每一个 id[i] 指向自己,没有合并的元素
        for (int i = 0; i < n; i++) {
            id[i] = i;
        }
    }

    // 查找过程,查找元素 p 所对应的集合编号
    // 复杂度 O(1)
    public int find(int p) {
        return id[p];
    }

    // 查看元素 p 和 元素 q 是否属于同一个集合
    // 复杂度 O(1)
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    // 合并元素 p 和元素 q 所属的集合
    // 复杂度 O(n)
    public void unionElements(int p, int q) {
        int pID = find(p);
        int qID = find(q);

        if (pID == qID)
            return;

        // 合并过程需要遍历一遍所有元素, 将两个元素的所属集合编号合并
        for (int i = 0; i < count; i++) {
            if (id[i] == pID)
                id[i] = qID;
        }
    }


}

测试

// 测试第一版本的并查集, 测试元素个数为n
    private static void testUnionFind1(int n) {
        UnionFind1 unionFind1 = new UnionFind1(n);

        long startTime = System.currentTimeMillis();

        // 进行 n 次操作,每次随机选择两个元素进行合并操作
        for (int i = 0; i < n; i++) {
            int a = (int) (Math.random() * n);
            int b = (int) (Math.random() * n);
            unionFind1.unionElements(a, b);
        }

        // 再进行n次操作, 每次随机选择两个元素, 查询他们是否同属一个集合
        for (int i = 0; i < n; i++) {
            int a = (int) (Math.random() * n);
            int b = (int) (Math.random() * n);
            unionFind1.isConnected(a, b);
        }

        long endTime = System.currentTimeMillis();

        // 打印输出对这2n个操作的耗时
        System.out.println("UnionFind_1 " + 2 * n + " ops, " + (endTime - startTime) + " ms ");
    }
作者:HeatDeath 发表于2017/11/16 15:16:02 原文链接
阅读:0 评论:0 查看评论

数据结构与算法分析(Java语言描述)(22)—— 并查集 Quick-Find

$
0
0

这里写图片描述

这里写图片描述

package com.dataStructure.union_find;

public class UnionFind2 {
    // parent[i] 表示第一个元素所指向的父节点
    private int[] parent;
    private int count;

    public UnionFind2(int n) {
        count = n;
        parent = new int[count];
        // 初始化,使每一个 parent[i] 指向自己,表示每一个元素自己自成一个集合
        for (int i = 0; i < count; i++) {
            parent[i] = i;
        }
    }

    // 查找过程,查找元素 p 所对应的集合编号
    // 复杂度O(h), h为树的高度
    private int find(int p){
        while (p!=parent[p])
            p = parent[p];
        return p;
    }

    // 查看元素p 和元素q 是否属于同一个集合
    // 复杂度O(h), h为树的高度
    public boolean isConnected(int p, int q){
        return find(p) == find(q);
    }

    // 合并元素p 和元素q 所属的集合
    // 复杂度O(h), h为树的高度
    public void unionElements(int p, int q){
        int pRoot = find(p);
        int qRoot = find(q);

        if (pRoot == qRoot)
            return;

        parent[pRoot] = qRoot;
    }


}
作者:HeatDeath 发表于2017/11/16 15:18:44 原文链接
阅读:0 评论:0 查看评论

数据结构与算法分析(Java语言描述)(23)—— 并查集基于 size 和 rank 的优化

$
0
0

基于 size 的优化

package com.dataStructure.union_find;

public class UnionFind3 {
    private int[] parent; // parent[i]表示第一个元素所指向的父节点
    private int[] size;     // sz[i]表示以i为根的集合中元素个数
    private int count;    // 数据个数

    public UnionFind3(int n) {
        count = n;
        parent = new int[count];
        size = new int[count];
        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for (int i = 0; i < count; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        // 不断去查询自己的父亲节点, 直到到达根节点
        // 根节点的特点: parent[p] == p
        while (p!=parent[p]){
            p = parent[p];
        }
        return p;
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    public boolean isConnected(int p, int q){
        return find(p) == find(q);
    }

    // 合并元素p和元素q所属的集合
    // O(h)复杂度, h为树的高度
    public void unionElements(int p, int q){
        int pRoot = find(p);
        int qRoot = find(q);

        if (pRoot == qRoot)
            return;

        // 根据两个元素所在树的元素个数不同判断合并方向
        // 将元素个数少的集合合并到元素个数多的集合上
        if (size[pRoot] > size[qRoot]){
            parent[qRoot] = pRoot;
            size[pRoot] += size[qRoot];
        }else {
            parent[pRoot] = qRoot;
            size[qRoot] += size[pRoot];
        }
    }
}

基于 rank 的优化

package com.dataStructure.union_find;

public class UnionFind4 {
    private int[] parent;   // parent[i]表示第i个元素所指向的父节点
    private int[] rank;     // rank[i]表示以i为根的集合所表示的树的层数
    private int count;      // 数据个数

    public UnionFind4(int n) {
        count = n;
        parent = new int[count];
        rank = new int[count];
        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for (int i = 0; i < count; i++) {
            parent[i] = i;
            rank[i] = 1;
        }
    }

    private int find(int p){
        // 查找过程, 查找元素p所对应的集合编号
        // O(h)复杂度, h为树的高度
        while (p!=parent[p]){
            // 不断去查询自己的父亲节点, 直到到达根节点
            // 根节点的特点: parent[p] == p
            p = parent[p];
        }
        return p;
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    public boolean isConnected(int p, int q){
        return find(p) == find(q);
    }

    // 合并元素p和元素q所属的集合
    // O(h)复杂度, h为树的高度
    public void unionElements(int p, int q){
        int pRoot = find(p);
        int qRoot = find(q);

        if (pRoot == qRoot)
            return;

        // 根据两个元素所在树的元素个数不同判断合并方向
        // 将元素个数少的集合合并到元素个数多的集合上
        if (rank[pRoot] > rank[qRoot])
            parent[qRoot] = pRoot;
        else if (rank[pRoot] < rank[qRoot])
            parent[pRoot] = qRoot;
        else{
            parent[pRoot] = qRoot;
            // 此时, 维护rank的值
            rank[qRoot] += 1;
        }

    }
}
作者:HeatDeath 发表于2017/11/16 15:23:57 原文链接
阅读:0 评论:0 查看评论

数据结构与算法分析(Java语言描述)(24)—— 并查集的路径压缩

$
0
0

这里写图片描述

这里写图片描述

package com.dataStructure.union_find;

// 我们的第五版Union-Find
public class UnionFind5 {

    // rank[i]表示以i为根的集合所表示的树的层数
    // 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
    // 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
    private int[] rank;
    private int[] parent; // parent[i]表示第i个元素所指向的父节点
    private int count;    // 数据个数

    // 构造函数
    public UnionFind5(int count){
        rank = new int[count];
        parent = new int[count];
        this.count = count;
        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for( int i = 0 ; i < count ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    // 查找过程, 查找元素p所对应的集合编号
    // O(h)复杂度, h为树的高度
    private int find(int p){
        assert( p >= 0 && p < count );

        // path compression 1
        while( p != parent[p] ){
            parent[p] = parent[parent[p]];
            p = parent[p];
        }
        return p;

        // path compression 2, 递归算法
//            if( p != parent[p] )
//                parent[p] = find( parent[p] );
//            return parent[p];
    }

    // 查看元素p和元素q是否所属一个集合
    // O(h)复杂度, h为树的高度
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合并元素p和元素q所属的集合
    // O(h)复杂度, h为树的高度
    public void unionElements(int p, int q){

        int pRoot = find(p);
        int qRoot = find(q);

        if( pRoot == qRoot )
            return;

        // 根据两个元素所在树的元素个数不同判断合并方向
        // 将元素个数少的集合合并到元素个数多的集合上
        if( rank[pRoot] < rank[qRoot] ){
            parent[pRoot] = qRoot;
        }
        else if( rank[qRoot] < rank[pRoot]){
            parent[qRoot] = pRoot;
        }
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此时, 我维护rank的值
        }
    }
}

在路径压缩的过程中,不需要继续维护 rank 了嘛?

事实上,这正是我们将这个变量叫做rank而不是叫诸如depth或者height的原因。

因为这个rank只是我们做的一个标志当前节点排名的一个数字,当我们引入了路径压缩以后,维护这个深度的真实值相对困难一些。

而且实践告诉我们,我们其实不需要真正维持这个值是真实的深度值,我们依然可以以这个rank值作为后续union过程的参考。

因为根据我们的路径压缩的过程,rank高的节点虽然被抬了上来,但是整体上,我们的并查集从任意一个叶子节点出发向根节点前进,依然是一个rank逐渐增高的过程。也就是说,这个rank值在经过路径压缩以后,虽然不是真正的深度值,但仍然可以胜任,作为union时的参考。
作者:HeatDeath 发表于2017/11/16 15:25:26 原文链接
阅读:2 评论:0 查看评论

【Java 并发】对象的组合

$
0
0

【Java 并发】对象的组合

一,在设计线程安全类时,需要关注以下三个基本要素:
1,找出构成对象状态的所有变量。
这些变量指的是对象的域,如果对象中的所有域都是基本类型的变量,那么这些域将构成对象的全部状态,比如Counter中的value,或者二维点的状态就是它的坐标值(x,y)。另外,如果对象的域中引用了其他对象,那么该对象的状态将包含被引用对象的域。例如,LinkedList的状态就包含链表中所有节点对象的状态。

2,找出约束状态变量的不变性条件
不变性条件,我的理解为约束或者规范,用来判断某个状态是否有效。这个不变性条件可以是对象本身就有,比如一个整型变量就有自己的空间范围,还可以是由开发人员根据需求定的,比如某个int变量不能为负数。还有其他不变性条件:
先验条件,例如,不能从空队列中移除一个元素,在删除元素前,需要判断队列是否处于“非空的”状态。也就是说操作依赖于状态。
后验条件,判断状态迁移,转换是否有效,比如,value++,当前状态是1,则下一个状态只能是2,。也就是下一个状态依赖于当前状态。
此外,如果不变性状态包含多个状态,那么就必须要做到保证原子性和封装性。

3,建立对象状态的并发访问策略
有相应的机制构建线程安全类

二,构建线程安全类
1,实例封闭机制
实例封闭机制是构造线程安全类的一种最简单方式。当一个对象被封装到另一个对象中时,访问被封装的对象的方式就很清晰。这比可以被整个程序访问更易于实现线程安全。说白了就是通过封闭,控制和限制访问对象的方式,并且这更加容易使用锁机制。示例代码

public class PersonSet{
@GuardedBy("this") 
private final Set<Person> mySet = new HashSet<Person>();
public synchronized void addPerson(Person p) {
    mySet.add(p);
}
public synchronized boolean containsPerson(Person p) {
    return mySet.contains(p);
}
}

PersonSet的状态都由HashSet管理,而HashSet是非线程安全的,将HashSet封闭在PersonSet,这样可以访问到mySet的方式只能通过addPerson和containsPerson,
在执行这两个方法时需要获得PersonSet的内置锁,所以在假设Person也是线程安全类的情况下,PersonSet就是线程安全类。
在封闭一个对象一定不能超出其设定的作用域,对象可以封闭为类的一个实例(私有成员),封闭在某个作用域(局部变量)和封闭在线程中。在使用时要注意逸出,比如前一篇博文中的在构造函数中声明匿名内部类或者启动线程。

2,线程安全性的委托
我们首先来考虑如果一个类只有一个线程安全的变量,那么很显然,这个类必然是线程安全的。

public class CountingFactorizer extends GenericServlet implements Servlet {
    private final AtomicLong count = new AtomicLong(0);

    public long getCount() { return count.get(); }

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        count.incrementAndGet();
        encodeIntoResponse(resp, factors);
    }
 }

在无状态的类中添加一个AtomicLong类型的域,并且得到的组合对象任然是线程安全的。

考虑多个线程安全变量
如果这个类的所有变量都是独立的:

public class VisualComponent {
    private final List<KeyListener> keyListeners
            = new CopyOnWriteArrayList<KeyListener>();
    private final List<MouseListener> mouseListeners
            = new CopyOnWriteArrayList<MouseListener>();

    public void addKeyListener(KeyListener listener) {
        keyListeners.add(listener);
    }

    public void addMouseListener(MouseListener listener) {
        mouseListeners.add(listener);
    }

    public void removeKeyListener(KeyListener listener) {
        keyListeners.remove(listener);
    }

    public void removeMouseListener(MouseListener listener) {
        mouseListeners.remove(listener);
    }
}

keyListeners和mouseListeners之间不存在任何关系,二者相互独立。CopyOnWriteArrayList也是一个线程安全链表,每个链表都是线程安全的,且各个状态之间不存在复合操作。VisualComponent 可以把线程安全委托给这两个变量。

如果这个类的所有变量存在依赖或者说关系时:

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException("can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException("can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

NumberRange 的两个状态变量都是AtomicInteger,看起来好像线程安全,但是两个变量存在着一个约束条件:lower <= upper。并且程序是“先检查后执行”,假设原来的范围是(0,10),一个线程调用setLower(5),同时另一个线程调用setUpper(4),由于缺少同步机制,两个线程调用都成功,则结果修改为(5.4)。这是个无效状态,虽然AtomicInteger线程安全,但是两个变量不相互独立,因此NumberRange不能把线程安全委托给这两个变量。

如果一个类是由多个独立且线程安全的状态变量组成,并且所有的操作都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量。

以上都是阅读《Java编程思想》-并发和《Java并发编程实践》的笔记,不喜勿喷!

作者:yangjjuan 发表于2017/11/16 15:26:17 原文链接
阅读:0 评论:0 查看评论

【蓝桥杯】【金蝉素数】

$
0
0

题目
考古发现某古墓石碑上刻着一个数字:13597,后研究发现:
这是一个素数!
并且,去掉首尾数字仍是素数!
并且,最中间的数字也是素数!
这样特征的数字还有哪些呢?通过以下程序的帮助可以轻松解决。
请仔细阅读代码,并填写划线部分缺失的代码。

分析
这是典型的dfs递归求所有可能性的问题,题目要求我们填写的是递归的出口。

源码

static boolean isPrime(int n){
        if(n<=1) return false;
        for(int i=2; i*i<=n; i++){
            if(n%i==0) return false;
        }
        return true;
    }

    static void f(int[] x, int k){
        if(k==x.length-1){  // 填空位置
            if(isPrime(x[0]*10000 + x[1]*1000 + x[2]*100 + x[3]*10 + x[4]) &&
                isPrime(x[1]*100 + x[2]*10 + x[3]) &&
                isPrime(x[2]))
                System.out.println(""+x[0]+x[1]+x[2]+x[3]+x[4]);
            return;
        }

        for(int i=k; i<x.length; i++){
            {int tmp=x[k]; x[k]=x[i]; x[i]=tmp; }
            f(x,k+1);
            {int tmp=x[k]; x[k]=x[i]; x[i]=tmp; }
        }
    }

    static void test()
    {
        int[] x = {1,3,5,7,9};
        f(x,0);
    }

    public static void main(String[] args)
    {
        test();
    }

结果
13597
53791
79531
95713
91573

作者:bear_huangzhen 发表于2017/11/16 15:32:47 原文链接
阅读:24 评论:0 查看评论

elasticsearch详解(三)——elasticsearch配置安装

$
0
0

1.安装环境

centos 7.2(可以使用docker就可以,后面用docker启动插件)
jdk1.8
elasticsearch 5.6

2.安装jdk

3.安装elasticsearch

从官网https://www.elastic.co/downloads下载es,这里使用比较新的5.6版本。

##下载
curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.4.tar.gz
##解压
tar -xzvf tar -xvf elasticsearch-5.6.4.tar.gz
##创建软连接
ln -s elasticsearch-5.6.4 elasticsearch

修改配置文件

cd elasticsearch
vi config/elasticsearch.yml
## 加入或修改下面的配置
## 设置自己的ip
network.host: 你的ip
## 用于head插件跨域访问用
http.cors.enabled: true
http.cors.allow-origin: "*"

es从2.0版本以后就不允许用root用户启动,考虑到权限安全问题

##增加es用户组
groupadd es
##增加es用户
useradd es -g es
##把elasticsearch文件夹权限赋予给es
chown -R es:es elasticsearch

此时如果切换到es用户启动会报错启动失败
max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
需要修改系统参数

##打开系统设置文件
vi /etc/security/limits.conf
##添加或修改es用户配置参数
es  -  nofile  65536

到这一步再切换到es用户操作。

##切换到es用户并启动es
su es
bin/elasticsearch &

启动日志里面可以看到节点启动了两个端口9200http服务接口9300TCP接口

4.安装head插件

从head插件开始熟悉认识elasticsearch。
head插件是es里面比较有名且好用的一个插件,在head里面可以查看es集群的状态、索引信息、创建索引、查询索引等

这里用docker安装head插件,用docker的原因就是少去配置环境的烦恼,方便。
安装docker的过程查看这里http://blog.csdn.net/dante_003/article/details/70208908

docker run -p 9100:9100 mobz/elasticsearch-head:5

启动后打开浏览器http://IP:9100
在上面的连接框里面输入http://IP:9200连接到elasticsearch的http接口
这里写图片描述
点击节点可以查看节点的信息

这里写图片描述

其它的可以查看分片状态。

在索引里面点击创建索引,设置分片和副本数量可以创建索引。
这里写图片描述

创建好后到概览里面查看索引状态。集群现在是黄色非健康状态,创建的索引5个分片也可以分配出去,原因是现在只有一个节点,副本碎片不会和原始碎片分布在一个节点上。

作者:Dante_003 发表于2017/11/16 15:40:26 原文链接
阅读:21 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>