-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsearch.xml
More file actions
97 lines (97 loc) · 49.9 KB
/
Copy pathsearch.xml
File metadata and controls
97 lines (97 loc) · 49.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Mongodb 数据备份和权限]]></title>
<url>%2F2017%2F08%2F02%2Fmongodb-shu-ju-bei-fen-he-quan-xian%2F</url>
<content type="text"><![CDATA[Mongodb 数据管理操作,导入导出,权限管理。 数据导出mongodump -h 127.0.0.1:27017 -d <db> -o <path> [-u -p] 数据导入mongorestore —host 127.0.0.1: 27017 -d <db> <path> 单表导出mongoexport -d <db> -c <table> -q ‘{“name”:{$ne:null}}’ -o <path>.json 单表导入mongoimport —host 127.0.0.1:27017 -d -c .json 数据清空mongo —host 127.0.0.1:19999 <db> —eval “db.dropDatabase()" 权限设置超级管理员db.createUser({ user: 'admin', pwd: 'admin', roles :[{ role: 'userAdminAnyDatabase', db: 'admin'} ]}) 授权 db.auth('admin','admin') 某个库的用户 // 在设置某个数据库的时候,先执行授权 use admin; db.auth('admin','admin'); use demo; db.createUser({ user: 'demo_user', pwd: ‘123456’, roles :[{ role: 'readWrite', db: 'demo' }]}) 备份用户 // 在设置某个数据库的时候,先执行授权 use admin; db.auth('admin','admin'); use demo; db.createUser({ user: 'demo_wheel', pwd: ‘123456’, roles :[{ role: 'read', db: 'demo' }]}) 开启授权认证sudo vi /etc/mongod.conf # 去掉注释 security: authorization: 'enabled' # 重启 sudo service mongod restart 访问某个库mongo 127.0.0.1:27017/<db> -u demo_user -p 123456 定时备份备份脚本#!/bin/bash backup_file=/home/backup/<db> date_now=`date +%Y_%m_%d_%H%M` backfile_name=movie_$date_now cd $backfile mkdir -p $backfile_name mongodump -h 127.0.0.1:27017 -d <db> -u user_wheel -p 123456 -o $backfile_name tar zcf $backfile_name.tar.gz $backfile_name rm -rf $backfile_name # 可以把备份文件上传到云服务器(七牛) #NODE_ENV=$backfile@$backfile_name node /home/upload.js 定时任务crontab -e # 凌晨13分 13 00 * * * sh /home/backup.sh document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>MongoDB</category>
<category>db</category>
</categories>
<tags>
<tag>mongodb</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Mongodb 教程]]></title>
<url>%2F2017%2F08%2F02%2Fmongodb-jiao-cheng%2F</url>
<content type="text"><![CDATA[MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。 安装社区版安装 MacMac系统 可以通过Homebrew安装 brew update brew install mongodb 要安装具有TLS / SSL支持的MongoDB二进制文件 brew install mongodb --with-openssl 安装最新开发版本 brew install mongodb --devel Linux 下载和安装 curl -O https://fastdl.mongodb.org/osx/mongodb-osx-x86_64-3.5.10.tgz tar -zxvf mongodb-osx-x86_64-3.5.10.tgz mkdir -p mongodb cp -R -n mongodb-osx-x86_64-3.5.10/ mongodb 配置环境变量 编辑 ~/.bashrc export PATH=<mongodb-install-directory>/bin:$PATH 运行 创建数据目录 在首次启动MongoDB之前,请创建mongod进程将写入数据的目录。默认情况下,mongod进程使用/data /db目录。如果创建一个除此之外的目录,则必须在此过程稍后再启动mongod进程时在dbpath选项中指定该目录。 设置数据目录读写权限 chmod 755 -R /data/db 运行 Mongo DB mongod # 默认使用/data/db # 如果你的PATH 没有加入mongod 的目录 <path to binary>/mongod # 可以通过 --dbpath 指定数据目录 mongod --dbpath <path to data directory> 开始使用Mongo DB停止运行,按键Ctrl + C 在mongod 所运行的终端 Mongo Shell要启动mongo shell并连接到在localhost上运行的MongoDB实例使用默认端口:27017 在终端窗口中提示(或Windows的命令提示符),进入您的mongodb安装目录:cd <mongodb installation dir> ./bin/mongo 可选参数 .mongorc.js 文件 启动时,mongo会检查用户的HOME目录,找到一个名为.mongorc.js的JavaScript文件。如果找到,mongo首先解释.mongorc.js的内容,然后再显示提示。如果您使用shell来评估JavaScript文件或表达式,可以通过在命令行中使用--eval选项,或者通过将.js文件指定为mongo,mongo将在JavaScript完成处理之后读取.mongorc.js文件。您可以通过使用--norc选项来阻止.mongorc.js被加载。 全局配置文件 UNIX 环境 /etc/mongorc.js WIndows 环境 %ProgramData%\ MongoDB 目录中。 使用 要使用身份验证和非标准端口连接到远程主机上的数据库,请使用以下格式: mongo --username --password --host --port 28015 或者简单形式 mongo -u <user> -p <pass> --host <host> --port 28015 要在启动shell会话之前不执行〜/ .mongorc.js文件执行JavaScript文件,请使用以下格式: mongo --shell --norc alternate-environment.js 要通过身份验证执行JavaScript文件,使用提示而不是命令行提供的密码,请使用以下格式: mongo script-file.js -u <user> -p 要打印以JSON格式返回查询,请使用--eval选项从系统提示符中使用以下格式: mongo --eval 'db.<collection>.find().forEach(printjson)' mongo db 配置文件 命令 mongod --config /etc/mongod.conf mongos --config /etc/mongos.conf # 或者 简化形式 mongod -f /etc/mongod.conf mongos -f /etc/mongos.conf 配置文件YAML注意yaml 不能使用制表符缩进,要用空格 systemLog: destination: file path: "/var/log/mongodb/mongod.log" logAppend: true storage: journal: enabled: true processManagement: fork: true net: bindIp: 127.0.0.1 port: 27017 setParameter: enableLocalhostAuthBypass: false 命令 CRUD 操作创建MongoDB提供以下方法将文档插入到集合中: db.collection.insertOne() db.collection.insertMany() 最新3.2 版本 在MongoDB中,插入操作指向单个集合。 MongoDB中的所有写入操作都是基于单个文档的级别的。 db.collection('inventory').insertMany([ { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" }}, { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" }}, { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" }} ]) .then(function(result) { //result 是一个_id 数组 }) 读取 db.collection.find() var cursor = db.collection('inventory') .find({ size: { h: 14, w: 21, uom: "cm" } }); // 高度小于 15 var cursor = db.collection('inventory').find({ "size.h": { $lt: 15 } }); 修改 db.collection.updateOne() db.collection.updateMany() db.collection.replaceOne() 删除 db.collection.deleteOne() db.collection.deleteMany() 全文搜索MongoDB支持执行字符串内容的文本搜索的查询操作。要执行文本搜索,MongoDB使用文本索引和$text操作符。例子 # 创建商店 db.stores.insert( [ { _id: 1, name: "Java Hut", description: "Coffee and cakes" }, { _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" }, { _id: 3, name: "Coffee Shop", description: "Just coffee" }, { _id: 4, name: "Clothes Clothes Clothes", description: "Discount clothing" }, { _id: 5, name: "Java Shopping", description: "Indonesian goods" } ] ) 全文索引 要执行文本搜索查询,您必须在集合上具有文本索引。一个集合只能有一个文本搜索索引,但该索引可以覆盖多个字段。 db.stores.createIndex( { name: "text", description: "text" } ) 操作您可以使用以下查询来查找包含“coffee”,“shop”和“java”列表中任何术语的所有商店: db.stores.find( { $text: { $search: "java coffee shop" } } ) 您还可以通过将其包装成双引号来搜索确切的短语。例如,以下将找到包含“java”或“咖啡店”的所有文档: db.stores.find( { $text: { $search: "java \"coffee shop\"" } } ) 要排除一个单词,您可以添加一个“ - ”字符。例如,要查找包含“java”或“shop”而不是“coffee”的所有商店,请使用以下命令: db.stores.find( { $text: { $search: "java shop -coffee" } } ) db.stores.find( { $text: { $search: "java coffee shop" } }, { score: { $meta: "textScore" } }) .sort( { score: { $meta: "textScore" } } ) 可视化管理工具MongoDB Cloud ManagerMongoDB Cloud Manager是官方推出的运维自动化管理系统,是企业版才支持的功能,社区用户也可以下载试用。 MongoDB 集群(复制集、分片)的自动化部署 集群监控、及报警定制 自动数据备份与还原 MongoDB compass MongoDB compass 也是官方推出的可视化管理工具,企业版才支持,compass 与 Cloud Manager 功能互补,Cloud Manager 偏向于部署、运维、监控,而 compass 则偏向于数据管理、查询优化等,其主要功能包括 数据分布可视化,自动分析各个字段取值的分布情况 支持 CRUD 操作 索引自动分析及优化建议 explain 的结果可视化 MongoDB Atlas MongoDB官方提供的 DBaaS 服务(Database as a Service),目前支持在 Amazon AWS 上构建 MongoDB 的云服务,未来有可能会支持更多的云厂商( 例如Azure、Alibaba Cloud等)并通过Cloud Manager + compass 来提供可视化的数据管理。 document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>MongoDB</category>
</categories>
<tags>
<tag>mongodb</tag>
<tag>db</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IOS抓包工具]]></title>
<url>%2F2016%2F11%2F23%2Fios-zhua-bao-gong-ju%2F</url>
<content type="text"><![CDATA[在iOS开发过程中,抓包是常见的必备技能之一。这里我们主要介绍一下Mac下的抓包利器Charles(文中版本:4.0.1)。它可以非常方便的抓取Http/Https请求,从而提高我们的开发效率。本文中不提供破解版安装使用说明(网上一大堆),建议使用正版,官方dmg下载地址:Charlesproxy 破解版:http://charles.iiilab.com) 如何抓取Http请求? 安装完成之后打开Charles,设置端口号:8888(端口号可以自定义)。 选择“Proxy”菜单下的“Proxy Settings”子菜单。打开“Proxy Settings”对话框: 在“Proxy Settings”对话框中设置端口号:8888,并勾选“Enable transparent Http proxying” 打开“网络偏好设置”查看电脑IP地址: 设置手机网络(iOS) 打开需要抓包的手机APP,初次使用时,Charles会弹出确认对话框,直接点击”Allow”按钮后就可以看到对应的请求数据 温馨提示:抓完包之后,请把手机WiFi中的HTTP代理关闭。不然可能造成iOS无法访问网络。 如何抓取Https请求? 电脑安装SSL证书 选中Charles,在“Help”菜单中选择—>“SSL Proxying”—>“Install Charles Root Certificate”会自动打开钥匙串访问窗口在“钥匙串访问”窗口中找到对应的证书,双击打开。设置“使用证书时”项为:始终信任。 填写管理员密码更新设置。 手机安装证书(使用Safari方式) 请确保手机已经设置好手动代理(具体方式参照上面HTTP抓包设置方式)。 在手机Safari浏览器中输入下面的链接地址:Charles Proxy。手机会自动跳转安装“Charles Proxy SSL Proxying”描述文件。如下图所示:点击“安装”按钮,会提示输入手机密码,然后确认安装。以上便完成手机SSL证书安装步骤。 在Charles工具栏上点击设置按钮,选择“SSL Proxying Settings…” 打开“SSL Proxying Settings”对话框 点击“Add”添加:Host中输入*表示匹配所有主机。https默认端口号:443添加完成显示结果如下 测试 安装手机证书之前测试结果如下图所示 安装手机证书之后测试结果如下图所示: 针对Charles代理访问https有些正常有些失败的处理方法(一般https只要打开charles 的”Enable SSL Proxying”代理就能访问) 如果没有Wifi,我们还可以这样抓包?(本文不做详解) 抓包之rvictl方式 开启虚拟端口:rvictl -s 关闭虚拟端口:rvictl -x 然后使用wireshark来捕捉这个端口数据。 抓包之tcpdump方式有个很大的缺点是手机需要越狱。 document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>https</tag>
<tag>ios</tag>
</tags>
</entry>
<entry>
<title><![CDATA[zookeeper教程]]></title>
<url>%2F2016%2F07%2F27%2Fzookeeper-jiao-cheng%2F</url>
<content type="text"><![CDATA[zookeeper 是一个开源的分布式协调服务,由知名互联网公司雅虎公司创建,是Google Chubby 开源实现。 他致力于提供一个高性能,高可用,且具有严格的顺序访问控制能力的分布式协调服务。分布式应用可以基于诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知,集群管理、Master选举、分布式锁和分布式队列。 基本概念zookeeper 应用场景数据的发布、订阅一方可以发布数据另一方接受数据 通常数据的订阅、发布 有两种方式:push / pullzk 采用两种方式相结合 发布者将数据发布到集群的节点上,订阅者通过一定的方法告诉服务器,我对那个节点的数据感兴趣,那个服务器在这些节点的数据发生变化时,就通知客户端,客户端得到通知后就可以去服务端获取数据 负载均衡DB 首先DB在启动的是先把自己在zk 注册成一个临时的节点,zk的节点有两种一种是永久节点,另一种是临时节点。临时节点在服务器出现问题的时候,节点会自动从zk上删除,那么这样zk上的服务器列表是最新的可用的列表 客户端需要读写数据库的时候首先去zk得到所有可用的DB连接信息(一张列表) 客户端随机选择一台进行连接 当客户端发现连接的时候再次从zk上获取可用的DB信息,当然也可以在刚获取的那个列表里移除掉不可用的连接后再选择一个DB连接 命名服务 顾名思义就是提供名称服务,例如数据库表格ID,一般有两种ID,一种是分布式中使用的,一种是自增长的ID,UUID 可以在分布式中使用但由于没有规律较难理解,友好性不好,我们可以使用zk来生成一个顺序增长的,可以在集群环境下使用。 分布式协调/通知心跳检测在分布式系统中,我们常常需要知道某个服务器是否可用,传统开发中,可以通过ping 某个主机。zk 中我们让所有机器都注册到一个临时节点,我们判断一个机器是否可用,我们只需判断这个节点zk是否存在就可以了,不需要直接去连接需要检查的机器,降低系统的复杂度。 环境搭建 安装 wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.3.6/ 解压 tar zxf zookeeper-3.3.6 && mv zookeeper-3.3.6 /usr/local/zookeeper 配置 vi /usr/local/<zookeeper>/conf/zoo.cnf ``` server.1=host:port:port-leader server.2=host:port:port-leader server.3=host:port:port-leader touch myid && echo "<id>" > myid ``` 启动/停止 ./zkServer start | stop 客户端连接 ./zkCli master选举架构图:程序流程图: 网络抖动: 核心类图: master 节点何时会删除 master 会主动放权 master 节点down机 master 节点网络斗动,会发生新一轮选举 如果master 和上一轮选举的master 不是同一个节点,不一致时发生资源迁移 优化:让上一轮选举出的master 再新一轮选举中优先采用master 节点 代码: package com.shaojun.zookeeper.mastersel; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.serialize.SerializableSerializer; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; public class LeaderSelectorZkClient { //启动的服务个数 private static final int CLIENT_QTY = 10; //zookeeper服务器的地址 private static final String ZOOKEEPER_SERVER = "10.3.1.11:2181,10.3.1.11:2182,10.3.1.11:2183"; public static void main(String[] args) throws Exception { //保存所有zkClient的列表 List<ZkClient> clients = new ArrayList<ZkClient>(); //保存所有服务的列表 List<WorkServer> workServers = new ArrayList<WorkServer>(); try { for ( int i = 0; i < CLIENT_QTY; ++i ) { //创建zkClient ZkClient client = new ZkClient(ZOOKEEPER_SERVER, 15000, 105000, new SerializableSerializer()); clients.add(client); //创建serverData RunningData runningData = new RunningData(); runningData.setCid(Long.valueOf(i)); runningData.setName("Client #" + i); //创建服务 WorkServer workServer = new WorkServer(runningData); workServer.setZkClient(client); workServers.add(workServer); workServer.start(); } System.out.println("敲回车键退出!\n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); } finally { System.out.println("Shutting down..."); for ( WorkServer workServer : workServers ) { try { workServer.stop(); } catch (Exception e) { e.printStackTrace(); } } for ( ZkClient client : clients ) { try { client.close(); } catch (Exception e) { e.printStackTrace(); } } } } } running data 实体类 package com.shaojun.zookeeper.mastersel; import java.io.Serializable; public class RunningData implements Serializable { private static final long serialVersionUID = 4260577459043203630L; private Long cid; private String name; public Long getCid() { return cid; } public void setCid(Long cid) { this.cid = cid; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 工作服务器类: package com.shaojun.zookeeper.mastersel; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkException; import org.I0Itec.zkclient.exception.ZkInterruptedException; import org.I0Itec.zkclient.exception.ZkNoNodeException; import org.I0Itec.zkclient.exception.ZkNodeExistsException; import org.apache.zookeeper.CreateMode; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class WorkServer { private volatile boolean running = false; private ZkClient zkClient; private static final String MASTER_PATH = "/master"; private IZkDataListener dataListener; private RunningData serverData; private RunningData masterData; private ScheduledExecutorService delayExector = Executors.newScheduledThreadPool(1); private int delayTime = 5; public WorkServer(RunningData rd) { this.serverData = rd; this.dataListener = new IZkDataListener() { public void handleDataDeleted(String dataPath) throws Exception { // TODO Auto-generated method stub //takeMaster(); if (masterData!=null && masterData.getName().equals(serverData.getName())){ takeMaster(); }else{ delayExector.schedule(new Runnable(){ public void run(){ takeMaster(); } }, delayTime, TimeUnit.SECONDS); } } public void handleDataChange(String dataPath, Object data) throws Exception { // TODO Auto-generated method stub } }; } public ZkClient getZkClient() { return zkClient; } public void setZkClient(ZkClient zkClient) { this.zkClient = zkClient; } public void start() throws Exception { if (running) { throw new Exception("server has startup..."); } running = true; zkClient.subscribeDataChanges(MASTER_PATH, dataListener); takeMaster(); } public void stop() throws Exception { if (!running) { throw new Exception("server has stoped"); } running = false; delayExector.shutdown(); zkClient.unsubscribeDataChanges(MASTER_PATH, dataListener); releaseMaster(); } private void takeMaster() { if (!running) return; try { zkClient.create(MASTER_PATH, serverData, CreateMode.EPHEMERAL); masterData = serverData; System.out.println(serverData.getName()+" is master"); delayExector.schedule(new Runnable() { public void run() { // TODO Auto-generated method stub if (checkMaster()){ releaseMaster(); } } }, 5, TimeUnit.SECONDS); } catch (ZkNodeExistsException e) { RunningData runningData = zkClient.readData(MASTER_PATH, true); if (runningData == null) { takeMaster(); } else { masterData = runningData; } } catch (Exception e) { // ignore; } } private void releaseMaster() { if (checkMaster()) { zkClient.delete(MASTER_PATH); } } private boolean checkMaster() { try { RunningData eventData = zkClient.readData(MASTER_PATH); masterData = eventData; if (masterData.getName().equals(serverData.getName())) { return true; } return false; } catch (ZkNoNodeException e) { return false; } catch (ZkInterruptedException e) { return checkMaster(); } catch (ZkException e) { return false; } } } 发布/订阅架构图:Manga Server 流程图: WorkServer 流程图: 系统核心类图: 代码: package com.shaojun.zookeeper.subscribe; import java.util.List; import org.I0Itec.zkclient.IZkChildListener; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkNoNodeException; import org.I0Itec.zkclient.exception.ZkNodeExistsException; import com.alibaba.fastjson.JSON; public class ManageServer { private String serversPath; private String commandPath; private String configPath; private ZkClient zkClient; private ServerConfig config; private IZkChildListener childListener; private IZkDataListener dataListener; private List<String> workServerList; public ManageServer(String serversPath, String commandPath, String configPath, ZkClient zkClient, ServerConfig config) { this.serversPath = serversPath; this.commandPath = commandPath; this.zkClient = zkClient; this.config = config; this.configPath = configPath; this.childListener = new IZkChildListener() { public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception { // TODO Auto-generated method stub workServerList = currentChilds; System.out.println("work server list changed, new list is "); execList(); } }; this.dataListener = new IZkDataListener() { public void handleDataDeleted(String dataPath) throws Exception { // TODO Auto-generated method stub // ignore; } public void handleDataChange(String dataPath, Object data) throws Exception { // TODO Auto-generated method stub String cmd = new String((byte[]) data); System.out.println("cmd:"+cmd); exeCmd(cmd); } }; } private void initRunning() { zkClient.subscribeDataChanges(commandPath, dataListener); zkClient.subscribeChildChanges(serversPath, childListener); } /* * 1: list 2: create 3: modify */ private void exeCmd(String cmdType) { if ("list".equals(cmdType)) { execList(); } else if ("create".equals(cmdType)) { execCreate(); } else if ("modify".equals(cmdType)) { execModify(); } else { System.out.println("error command!" + cmdType); } } private void execList() { System.out.println(workServerList.toString()); } private void execCreate() { if (!zkClient.exists(configPath)) { try { zkClient.createPersistent(configPath, JSON.toJSONString(config) .getBytes()); } catch (ZkNodeExistsException e) { zkClient.writeData(configPath, JSON.toJSONString(config) .getBytes()); } catch (ZkNoNodeException e) { String parentDir = configPath.substring(0, configPath.lastIndexOf('/')); zkClient.createPersistent(parentDir, true); execCreate(); } } } private void execModify() { config.setDbUser(config.getDbUser() + "_modify"); try { zkClient.writeData(configPath, JSON.toJSONString(config).getBytes()); } catch (ZkNoNodeException e) { execCreate(); } } public void start() { initRunning(); } public void stop() { zkClient.unsubscribeChildChanges(serversPath, childListener); zkClient.unsubscribeDataChanges(commandPath, dataListener); } } Server Config: package com.shaojun.zookeeper.subscribe; public class ServerConfig { private String dbUrl; private String dbPwd; private String dbUser; public String getDbUrl() { return dbUrl; } public void setDbUrl(String dbUrl) { this.dbUrl = dbUrl; } public String getDbPwd() { return dbPwd; } public void setDbPwd(String dbPwd) { this.dbPwd = dbPwd; } public String getDbUser() { return dbUser; } public void setDbUser(String dbUser) { this.dbUser = dbUser; } @Override public String toString() { return "ServerConfig [dbUrl=" + dbUrl + ", dbPwd=" + dbPwd + ", dbUser=" + dbUser + "]"; } } Server data package com.shaojun.zookeeper.subscribe; public class ServerData { private String address; private Integer id; private String name; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "ServerData [address=" + address + ", id=" + id + ", name=" + name + "]"; } } 订阅发布入口: package com.shaojun.zookeeper.subscribe; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer; public class SubscribeZkClient { private static final int CLIENT_QTY = 5; private static final String ZOOKEEPER_SERVER = "192.168.1.105:2181"; private static final String CONFIG_PATH = "/config"; private static final String COMMAND_PATH = "/command"; private static final String SERVERS_PATH = "/servers"; public static void main(String[] args) throws Exception { List<ZkClient> clients = new ArrayList<ZkClient>(); List<WorkServer> workServers = new ArrayList<WorkServer>(); ManageServer manageServer = null; try { ServerConfig initConfig = new ServerConfig(); initConfig.setDbPwd("123456"); initConfig.setDbUrl("jdbc:mysql://localhost:3306/mydb"); initConfig.setDbUser("root"); ZkClient clientManage = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer()); manageServer = new ManageServer(SERVERS_PATH, COMMAND_PATH,CONFIG_PATH,clientManage,initConfig); manageServer.start(); for ( int i = 0; i < CLIENT_QTY; ++i ) { ZkClient client = new ZkClient(ZOOKEEPER_SERVER, 5000, 5000, new BytesPushThroughSerializer()); clients.add(client); ServerData serverData = new ServerData(); serverData.setId(i); serverData.setName("WorkServer#"+i); serverData.setAddress("192.168.1."+i); WorkServer workServer = new WorkServer(CONFIG_PATH, SERVERS_PATH, serverData, client, initConfig); workServers.add(workServer); workServer.start(); } System.out.println("敲回车键退出!\n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); } finally { System.out.println("Shutting down..."); for ( WorkServer workServer : workServers ) { try { workServer.stop(); } catch (Exception e) { e.printStackTrace(); } } for ( ZkClient client : clients ) { try { client.close(); } catch (Exception e) { e.printStackTrace(); } } } } } Work Server 类: package com.shaojun.zookeeper.subscribe; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkNoNodeException; import com.alibaba.fastjson.JSON; public class WorkServer { private ZkClient zkClient; private String configPath; private String serversPath; private ServerData serverData; private ServerConfig serverConfig; private IZkDataListener dataListener; public WorkServer(String configPath, String serversPath, ServerData serverData, ZkClient zkClient, ServerConfig initConfig) { this.zkClient = zkClient; this.serversPath = serversPath; this.configPath = configPath; this.serverConfig = initConfig; this.serverData = serverData; this.dataListener = new IZkDataListener() { public void handleDataDeleted(String dataPath) throws Exception { // TODO Auto-generated method stub } public void handleDataChange(String dataPath, Object data) throws Exception { // TODO Auto-generated method stub String retJson = new String((byte[])data); ServerConfig serverConfigLocal = (ServerConfig)JSON.parseObject(retJson,ServerConfig.class); updateConfig(serverConfigLocal); System.out.println("new Work server config is:"+serverConfig.toString()); } }; } public void start() { System.out.println("work server start..."); initRunning(); } public void stop() { System.out.println("work server stop..."); zkClient.unsubscribeDataChanges(configPath, dataListener); } private void initRunning() { registMe(); zkClient.subscribeDataChanges(configPath, dataListener); } private void registMe() { String mePath = serversPath.concat("/").concat(serverData.getAddress()); try { zkClient.createEphemeral(mePath, JSON.toJSONString(serverData) .getBytes()); } catch (ZkNoNodeException e) { zkClient.createPersistent(serversPath, true); registMe(); } } private void updateConfig(ServerConfig serverConfig) { this.serverConfig = serverConfig; } } 负载均衡架构图 客户端流程 服务端主体流程图 Server 核心类图 代码结构:客户端服务端 命名服务架构图核心算法代码 package com.shaojun.zookeeper.nameservice; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkNodeExistsException; import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class IdMaker { private ZkClient client = null; private final String server; private final String root; private final String nodeName; private volatile boolean running = false; private ExecutorService cleanExector = null; public enum RemoveMethod{ NONE,IMMEDIATELY,DELAY } public IdMaker(String zkServer,String root,String nodeName){ this.root = root; this.server = zkServer; this.nodeName = nodeName; } public void start() throws Exception { if (running) throw new Exception("server has stated..."); running = true; init(); } public void stop() throws Exception { if (!running) throw new Exception("server has stopped..."); running = false; freeResource(); } private void init(){ client = new ZkClient(server,5000,5000,new BytesPushThroughSerializer()); cleanExector = Executors.newFixedThreadPool(10); try{ client.createPersistent(root,true); }catch (ZkNodeExistsException e){ //ignore; } } private void freeResource(){ cleanExector.shutdown(); try{ cleanExector.awaitTermination(2, TimeUnit.SECONDS); }catch(InterruptedException e){ e.printStackTrace(); }finally{ cleanExector = null; } if (client!=null){ client.close(); client=null; } } private void checkRunning() throws Exception { if (!running) throw new Exception("请先调用start"); } private String ExtractId(String str){ int index = str.lastIndexOf(nodeName); if (index >= 0){ index+=nodeName.length(); return index <= str.length()?str.substring(index):""; } return str; } public String generateId(RemoveMethod removeMethod) throws Exception{ checkRunning(); final String fullNodePath = root.concat("/").concat(nodeName); final String ourPath = client.createPersistentSequential(fullNodePath, null); if (removeMethod.equals(RemoveMethod.IMMEDIATELY)){ client.delete(ourPath); }else if (removeMethod.equals(RemoveMethod.DELAY)){ cleanExector.execute(new Runnable() { public void run() { // TODO Auto-generated method stub client.delete(ourPath); } }); } //node-0000000000, node-0000000001 return ExtractId(ourPath); } } 测试 package com.shaojun.zookeeper.nameservice; public class TestIdMaker { public static void main(String[] args) throws Exception { IdMaker idMaker = new IdMaker("localhost:2181", "/NameService/IdGen", "ID"); idMaker.start(); try { for (int i = 0; i < 10; i++) { String id = idMaker.generateId(IdMaker.RemoveMethod.DELAY); System.out.println(id); } } finally { idMaker.stop(); } } } 分布式队列架构图offer 核心算法poll 核心算法核心代码简单的队列 package com.shaojun.zookeeper.queue; import org.I0Itec.zkclient.ExceptionUtil; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkNoNodeException; import java.util.Collections; import java.util.Comparator; import java.util.List; public class DistributedSimpleQueue<T> { protected final ZkClient zkClient; protected final String root; protected static final String Node_NAME = "n_"; public DistributedSimpleQueue(ZkClient zkClient, String root) { this.zkClient = zkClient; this.root = root; } public int size() { return zkClient.getChildren(root).size(); } public boolean isEmpty() { return zkClient.getChildren(root).size() == 0; } public boolean offer(T element) throws Exception{ String nodeFullPath = root .concat( "/" ).concat( Node_NAME ); try { zkClient.createPersistentSequential(nodeFullPath , element); }catch (ZkNoNodeException e) { zkClient.createPersistent(root); offer(element); } catch (Exception e) { throw ExceptionUtil.convertToRuntimeException(e); } return true; } @SuppressWarnings("unchecked") public T poll() throws Exception { try { List<String> list = zkClient.getChildren(root); if (list.size() == 0) { return null; } Collections.sort(list, new Comparator<String>() { public int compare(String lhs, String rhs) { return getNodeNumber(lhs, Node_NAME).compareTo(getNodeNumber(rhs, Node_NAME)); } }); for ( String nodeName : list ){ String nodeFullPath = root.concat("/").concat(nodeName); try { T node = (T) zkClient.readData(nodeFullPath); zkClient.delete(nodeFullPath); return node; } catch (ZkNoNodeException e) { // ignore } } return null; } catch (Exception e) { throw ExceptionUtil.convertToRuntimeException(e); } } private String getNodeNumber(String str, String nodeName) { int index = str.lastIndexOf(nodeName); if (index >= 0) { index += Node_NAME.length(); return index <= str.length() ? str.substring(index) : ""; } return str; } } 扩展队列 package com.shaojun.zookeeper.queue; import org.I0Itec.zkclient.IZkChildListener; import org.I0Itec.zkclient.ZkClient; import java.util.List; import java.util.concurrent.CountDownLatch; public class DistributedBlockingQueue<T> extends DistributedSimpleQueue<T>{ public DistributedBlockingQueue(ZkClient zkClient, String root) { super(zkClient, root); } @Override public T poll() throws Exception { while (true){ final CountDownLatch latch = new CountDownLatch(1); final IZkChildListener childListener = new IZkChildListener() { public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception { latch.countDown(); } }; zkClient.subscribeChildChanges(root, childListener); try{ T node = super.poll(); if ( node != null ){ return node; }else{ latch.await(); } }finally{ zkClient.unsubscribeChildChanges(root, childListener); } } } } 测试类 package com.shaojun.zookeeper.queue; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.serialize.SerializableSerializer; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class TestDistributedBlockingQueue { public static void main(String[] args) { ScheduledExecutorService delayExector = Executors.newScheduledThreadPool(1); int delayTime = 5; ZkClient zkClient = new ZkClient("localhost:2181", 5000, 5000, new SerializableSerializer()); final DistributedBlockingQueue<User> queue = new DistributedBlockingQueue<User>(zkClient,"/Queue"); final User user1 = new User(); user1.setId("1"); user1.setName("xiao wang"); final User user2 = new User(); user2.setId("2"); user2.setName("xiao wang"); try { delayExector.schedule(new Runnable() { public void run() { try { queue.offer(user1); queue.offer(user2); } catch (Exception e) { e.printStackTrace(); } } }, delayTime , TimeUnit.SECONDS); System.out.println("ready poll!"); User u1 = (User) queue.poll(); User u2 = (User) queue.poll(); if (user1.getId().equals(u1.getId()) && user2.getId().equals(u2.getId())){ System.out.println("Success!"); } } catch (Exception e) { e.printStackTrace(); } finally{ delayExector.shutdown(); try { delayExector.awaitTermination(2, TimeUnit.SECONDS); } catch (InterruptedException e) { } } } } 分布式锁架构图 核心算法流程图 全局事务锁的相关代码 document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>zookeeper</tag>
</tags>
</entry>
<entry>
<title><![CDATA[在线工具列表]]></title>
<url>%2F2016%2F05%2F09%2Fzai-xian-gong-ju-lie-biao%2F</url>
<content type="text"><![CDATA[程序员的工具箱。 Java源代码搜索 Grepcode是一个面向于Java开发人员的网站,在这里你可以通过Java的projects、classes等各种关键字在线查看它对应的源码,知道对应的project、classes等信息。 更方便的是,能提供非常多不同版本的源码在线查看、jar包、源码jar包、doc的下载。 同样,你也可以之间使用xxx-1.1.1.jar类似这样的名字直接找到对应的jar包,从而下载。 开源代码及文档搜索 Searchcode是一个源码搜索引擎,目前支持从 Github、Bitbucket、Google Code、CodePlex、SourceForge 和 Fedora Project 平台搜索公开的源码。 在线UML制图 Processon是一个在线协作绘图平台,为用户提供最强大、易用的作图工具!支持在线创作流程图、BPMN、UML图、UI界面原型设计、iOS界面原型设计等。 Json在线验证及格式化 我用过很多json在线格式化的工具,经过实践,json.cn是比较不错的,不仅支持json格式的验证及格式化,还可以将json格式压缩成普通文本等好用功能。 Diff Check 使用过svn或者git的人对diffcheck肯定不陌生,但有时候我们修改的文本内容并没有被版本控制,那么就可以使用在线的网站查看文件的修改情况。diffchecker很不错。 MarkDown编辑器 Cmd segmentfault Mou Mac平台下我的最爱 构建工具Maven依赖查询 mvnrepository这个不用详细解释了,就是查询maven的gav信息一类的。因为我们公司有内部的Nexus仓库,所以很少用这个。 Apache Maven Apache Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建。Maven优于Apache Ant。后者采用了一种过程化的方式进行配置,所以维护起来相当困难。 Gradle GradleGradle采用增量构建。Gradle通过Groovy编程而不是传统的XML声明进行配置。Gradle可以很好地配合Maven进行依赖管理,并且把Ant脚本当作头等公民。 在线代码运行 coderunner可以在线运行php,c,c++,go,python,java,groovy等代码。基本是很少使用。 在线翻译谷歌翻译百度翻译有道翻译 SQL自动生成Java代码 AutoJCode可以从sql的建表语句中生成一个DO类。 json生成java类json2java SQL美化/格式化/压缩sql在线美化,格式化,压缩 编码转换编码转换站长工具的编码转换比较全面,提供了Unicode编码、UFT8编码、URL编码/解码等功能。 Corn表达式生成 Cron一般用于配置定时任务的执行。没有什么特别好的网站,pdtools 还可以吧,基本可以满足需求。 正则验证 站长工具提供的正则代码生成。 时间戳转换 时间戳 (英语:Timestamp)是指在一连串的资料中加入辨识文字,如时间或日期,用以保障本地端(local)资料更新顺序与远端(remote)一致。 站长工具提供的时间戳转换。 世界时间转换世界各地时间转换,我比较常用的是北京时间转纽约时间,北京时间转洛杉矶时间。 timebie提供了世界时间相互转换的功能。 加密解密 站长工具中的加密解密 单位换算 convertworld是一个比较全的单位换算的网站。我经常用它进行时间单位和货币单位的换算。 在线调色板 在线调色板 常用对照表 ASCII对照表 HTTP状态码 HTTP Content-type TCP/UDP常见端口参考 HTML转义字符 RGB颜色参考 欢迎补充~ document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[nio文件读写操作]]></title>
<url>%2F2016%2F04%2F12%2Fnio-wen-jian-du-xie-cao-zuo%2F</url>
<content type="text"><![CDATA[nio 是New IO 的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。 – 摘抄百度百科 代码import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Date; public class NIO { public static void main(String args[]) throws Exception { int bufSize = 1000000;//一次读取的字节长度 File fin = new File("D:\\test\\readFile.txt");//读取的文件 File fout = new File("D:\\test\\writeFile.txt");//写出的文件 Date startDate = new Date(); FileChannel fcin = new RandomAccessFile(fin, "r").getChannel(); ByteBuffer rBuffer = ByteBuffer.allocate(bufSize); FileChannel fcout = new RandomAccessFile(fout, "rws").getChannel(); ByteBuffer wBuffer = ByteBuffer.allocateDirect(bufSize); readFileByLine(bufSize, fcin, rBuffer, fcout, wBuffer); Date endDate = new Date(); System.out.print(startDate+"|"+endDate);//测试执行时间 } public static void readFileByLine(int bufSize, FileChannel fcin, ByteBuffer rBuffer, FileChannel fcout, ByteBuffer wBuffer) { String enterStr = "\n"; try { byte[] bs = new byte[bufSize]; //temp:由于是按固定字节读取,在一次读取中,第一行和最后一行经常是不完整的行,因此定义此变量来存储上次的最后一行和这次的第一行的内容, //并将之连接成完成的一行,否则会出现汉字被拆分成2个字节,并被提前转换成字符串而乱码的问题,数组大小应大于文件中最长一行的字节数 byte[] temp = new byte[500]; while (fcin.read(rBuffer) != -1) { int rSize = rBuffer.position(); rBuffer.rewind(); rBuffer.get(bs); rBuffer.clear(); //windows下ascii值13、10是换行和回车,unix下ascii值10是换行 //从开头顺序遍历,找到第一个换行符 int startNum=0; int length=0; for(int i=0;i<rSize;i++){ if(bs[i]==10){//找到换行字符 startNum=i; for(int k=0;k<500;k++){ if(temp[k]==0){//temp已经存储了上一次读取的最后一行,因此遍历找到空字符位置,继续存储此次的第一行内容,连接成完成一行 length=i+k; for(int j=0;j<=i;j++){ temp[k+j]=bs[j]; } break; } } break; } } //将拼凑出来的完整的一行转换成字符串 String tempString1 = new String(temp, 0, length+1, "GBK"); //清空temp数组 for(int i=0;i<temp.length;i++){ temp[i]=0; } //从末尾倒序遍历,找到第一个换行符 int endNum=0; int k = 0; for(int i=rSize-1;i>=0;i--){ if(bs[i]==10){ endNum=i;//记录最后一个换行符的位置 for(int j=i+1;j<rSize;j++){ temp[k++]=bs[j];//将此次读取的最后一行的不完整字节存储在temp数组,用来跟下一次读取的第一行拼接成完成一行 bs[j]=0; } break; } } //去掉第一行和最后一行不完整的,将中间所有完整的行转换成字符串 String tempString2 = new String(bs, startNum+1, endNum-startNum, "GBK"); //拼接两个字符串 String tempString = tempString1 + tempString2; // System.out.print(tempString); int fromIndex = 0; int endIndex = 0; while ((endIndex = tempString.indexOf(enterStr, fromIndex)) != -1) { String line = tempString.substring(fromIndex, endIndex)+enterStr;//按行截取字符串 System.out.print(line); //写入文件 writeFileByLine(fcout, wBuffer, line); fromIndex = endIndex + 1; } } } catch (IOException e) { e.printStackTrace(); } } /** * 写到文件上 * @param fcout * @param wBuffer * @param line */ @SuppressWarnings("static-access") public static void writeFileByLine(FileChannel fcout, ByteBuffer wBuffer, String line) { try { fcout.write(wBuffer.wrap(line.getBytes("UTF-8")), fcout.size()); } catch (IOException e) { e.printStackTrace(); } } } document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>java</category>
</categories>
<tags>
<tag>java</tag>
<tag>NIO</tag>
</tags>
</entry>
<entry>
<title><![CDATA[我的阅历]]></title>
<url>%2F2015%2F12%2F23%2Fwo-de-yue-li%2F</url>
<content type="text"><![CDATA[不读书是瞒不住人的。—— 不如 读书根本无用,但是充满了知道的快乐。—— 微博某 “非”『著』名『程序』猿 书富如入海,百货皆有。人之精力,不能兼收尽取,但得春所欲求者尔。故愿学者每次作一意求之。 —— 苏轼『东坡文集事略』 document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>经历</category>
</categories>
<tags>
<tag>读书</tag>
<tag>Photo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[简单美观的文字标签云组件]]></title>
<url>%2F2015%2F12%2F03%2Fbeautiful-word-cloud%2F</url>
<content type="text"><![CDATA[经常在微博或微信的文章中看到漂亮的分析图。我认为在大数据的时代,目前最关键的就是如何让非专业人员轻松的进行数据分析,比如可以象使用office一样制作信息图(infographic),而不是用专业的制图工具。这一步跨过去,看到的将是欣欣向荣的真正大数据时代。 而这之前,首先缺少的就是,可以让普通开发人员使用的大数据时代的可视化图表组件,比如标签云图,所幸,业界已经有ECharts和WordCloud这两大利器,本文只介绍后者。 首先页面必须是html5编写。 <!DOCTYPE html><html> <head> <meta charset=“UTF-8”> <title></title> </head> <body> </body></html> 引入jQuery和WordCloud2.js。 <script src=“src/wordcloud2.js”></script><script src=“//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js”></script> 定义canvas容器。 <div id=“canvas-container” align=“center”> <canvas id=“canvas” width=“800px” height=“600px”></canvas></div> 绘图。 <script> var options = eval({ “list”: [[‘傻猎豹’, 10], [‘不如’, 9], [‘麻花疼’, 7], [‘麻云’, 6],[‘李眼红’, 4], [‘雷布斯’, 5],[‘周红衣’, 4],[‘刘墙洞’, 3],[‘李国情’, 3]], “gridSize”: 8, “weightFactor”: 16, “fontFamily”: ‘Hiragino Mincho Pro, serif’, “color”: ‘random-dark’, “backgroundColor”: ‘#f0f0f0’, “rotateRatio”: 0});var canvas = document.getElementById(‘canvas’);WordCloud(canvas, options);</script>> 至此,全部完毕。执行页面,美丽的云图便展现在你面前,具体的API可以参考这里。下面举个英文的例子,为了美观稍微改变一下参数:“list”: [[‘bruce-sha’, 10], [‘buru’, 9], [‘tencent’, 7], [‘alibaba’, 6], [‘baidu’, 4], [‘xiaomi’, 5],[‘360’, 4],[‘jingdong’, 3],[‘dangdang’, 3],[‘ibruce.info’, 1]],“gridSize”: 16,“weightFactor”: 16,“fontFamily”: ‘Times, serif’,“color”: ‘random-light’,“backgroundColor”: ‘#333’,“rotateRatio”: 0 document.querySelectorAll('.github-emoji') .forEach(el => { if (!el.dataset.src) { return; } const img = document.createElement('img'); img.style = 'display:none !important;'; img.src = el.dataset.src; img.addEventListener('error', () => { img.remove(); el.style.color = 'inherit'; el.style.backgroundImage = 'none'; el.style.background = 'none'; }); img.addEventListener('load', () => { img.remove(); }); document.body.appendChild(img); });]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>javascript</tag>
<tag>WordCloud</tag>
</tags>
</entry>
</search>