Kubernetes Controller详解 - deachiy - 博客园

mikel阅读(1146)

来源: Kubernetes Controller详解 – deachiy – 博客园


运行容器化应用是Kubernetes最重要的核心功能。为满足不同的业务需要,Kubernetes提供了多种Controller,主要包括Deployment、DaemonSet、Job、CronJob等。
1、创建资源的两种方式
创建资源主要有通过命令行配置参数和通过配置文件这两种方式。
通过命令行主要是使用kubectl命令来进行创建,主要可能用到的是kubectl run和kubectl create,具体的用法我们可以在命令后面加上–-help参数来查看帮助文档。
这种方式的好处就是简单快捷,部署的速度比较快,但是遇到要求比较复杂多样的资源部署,后面就要附带一大串参数,容易出错,所以这种方式一般来说比较适用于小规模的简单资源部署或者是上线前的简单测试
通过配置文件则主要是json格式或yaml格式的文件,好处是可以详细配置各种参数,保留的配置文件还可以用到其他的集群上进行大规模的部署操作,缺点就是部署比较麻烦,并且需要一定的门槛(要求对json或yaml有一定的了解)
配置文件主要是通过kubectl apply -f和kubectl create -f来进行配置。
2、Deployment
2.1 cli部署
我们先使用命令行(cli)创建一个deployment,名字(NAME)是nginx-clideployment,使用的镜像(image)版本为1.17,创建的副本(replicas)数量为3。
kubectl run nginx-clideployment –image=nginx:1.17 –replicas=3
我们查看一下部署是否成功:

这里的kubectl get rs中的rs其实就是ReplicaSet(RS)
我们还可以发现ReplicaSet的命名就是在我们指定的NAME后面加上了一串哈希数值。
-.
想要查看更详细的pod情况,我们可以这样:
kubectl get pod -o wide

这里我们可以发现三个pod被k8s自动的分配到了三个节点上而实现集群中的负载均衡(LB),而这里的IP是创建pod的时候进行随机分配的,我们并不能预知。
如果想要查看某一个pod部署之后的情况,我们可以这样:
kubectl describe pods nginx-clideployment-5696d55d9d-gdtrt
如果我们只是输入nginx-clideployment的话就会把所有相关的nginx-clideployment都列出来。

这里的信息很多,我们可以看到namespace是使用的默认default(一般都是default,除非是数十人以上在同时使用这个集群,否则一般不建议新建namespace来区分pod,大多数情况下使用label即可区分各类pod。)
Controlled By:则说明了这个deployment是由ReplicaSet控制的。
Conditions:则表明了当前的pod状况

Volumes称之为卷,这个概念我们暂时还没有接触到,等到我们的宿主机需要与容器内的服务进行数据交互的时候再进行了解。
Events则相当于log,记录了pod运行的所有情况,如果遇到了运行不正常的情况,我们也可以查看这里来了解详情。

2.2 yaml配置文件部署
接下来我们尝试使用yaml文件来进行部署,新建一个文件,命名为nginx-yamldeployment.yml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-yamldeployment
spec:
replicas: 5
template:
metadata:
labels:
app: web_server
spec:
containers:
– name: nginx
image: nginx:1.17.1
apiVersion是当前配置格式的版本。
kind是要创建的资源类型,这里是Deployment。
metadata是该资源的元数据,name是必需的元数据项。
spec部分是该Deployment的规格说明。
replicas 指明副本数量,默认为1。
template 定义Pod的模板,这是配置文件的重要部分。
metadata定义Pod的元数据,至少要定义一个label。label的key和value可以任意指定。
spec 描述Pod的规格,此部分定义Pod中每一个容器的属性,name和image是必需的。
接下来我们使用kubectl apply指令进行部署。
kubectl apply -f nginx-yamldeployment.yml


我们可以看到这里是已经运行正常了,那么如果我们想要修改这个deployment的属性,比如说副本数量从5改为6,那么我们可以直接编辑刚刚的nginx-yamldeployment.yml文件,然后再执行kubectl apply -f nginx-yamldeployment.yml,但是如果文件找不到了,我们可以使用另外的方法:
kubectl edit deployments.apps nginx-yamldeployment
这样子我们就能直接编辑这个deployment的yaml配置文件,编辑的操作方式和vim相同,修改完成后会自动生效。

我们修改保存退出后,可以看到这里已经生效了。

对于使用命令行创建的deployment,我们可以使用命令行来进行修改,也可以直接kubectl edit来编辑对应的yaml配置文件。

3、DaemonSet
我们先来看一下官网对DaemonSet的解释:
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时,也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。
使用 DaemonSet 的一些典型用法:
运行集群存储 daemon,例如在每个节点上运行 glusterd、ceph。
在每个节点上运行日志收集 daemon,例如fluentd、logstash。
在每个节点上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond。
一个简单的用法是在所有的节点上都启动一个 DaemonSet,将被作为每种类型的 daemon 使用。 一个稍微复杂的用法是单独对每种 daemon 类型使用多个 DaemonSet,但具有不同的标志,和/或对不同硬件类型具有不同的内存、CPU要求。
实际上,k8s本身的一些系统组件服务就是以DaemonSet的形式运行在各个节点上的。

4、Job
4.1 部署job
Job创建一个或多个Pod并确保指定数量的Pod成功终止。当pod成功完成后,Job会跟踪成功的完成情况。达到指定数量的成功完成时,Job完成。
删除Job将清理它创建的Pod。
一个简单的例子是创建一个Job对象,以便可靠地运行一个Pod并成功完成指定任务。如果第一个Pod失败或被删除(例如由于节点硬件故障或节点重启),Job对象将启动一个新的Pod。
我们还可以使用Job并行运行多个Pod。
直接照搬官网的解释可能会有些难以理解,我们来运行一个例子就能很好的说明情况了。
我们先新建一个job的配置文件,命名为pijob.yml,这个任务是将pi计算到小数点后两千位,然后再打印出来。我们运行一下看看。

apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
– name: pi
image: perl
command: [“perl”, “-Mbignum=bpi”, “-wle”, “print bpi(2000)”]
restartPolicy: Never
backoffLimit: 4

我们查看它的详细情况
kubectl describe jobs/pi

这里可以看到,运行了37s后成功计算出结果。

查看pod也可以看到它的状态是Completed,说明已经完成了。

查看创建的pod的名称

pods=$(kubectl get pods –selector=job-name=pi –output=jsonpath='{.items[*].metadata.name}’)
echo $pods

查看pod的运行结果

kubectl logs $pods

4.2 job运行错误
我们现在来看一下运行失败的情况,我们把command里面的路径改错,使得它无法正常运行。

注意这里的restartPolicy: Never意味着pod运行失败了也不会重启。但是这个时候job会检测到运行失败,然后再新建一个pod来执行这个任务。
backoffLimit: 4意味着最多只会新建4个pod,避免一直失败一直新建从而耗尽系统资源。

我们查看event可以看到,确实是由于路径修改错误而导致无法正常运行。

接下来我们把restartPolicy: 改为 OnFailure
这时候我们可以看到并没有启动多个pod,二是对发生错误的pod进行重启操作。

4.3 job并行化
在多线程早已普及的今天,很多任务我们都可以使用并行化来进行加速运行,这里也不例外。
我们在配置文件中的spec中加入 completions: 和parallelism: 。

图中表示需要运行12个,每次并行运行6个。

从图中我们可以看到确实是每次运行6个(运行时间相同说明同时开始运行)。
等待一段时候之后,我们再次查看而已看到任务已经顺利完成了。

5、CronJob
熟悉linux的同学一定不会对cron感到陌生,因为cron就是用来管理linux中的定时任务的工具,所以CronJob我们可以理解为定时版的Job,其定时任务的编写格式也和Cron相似。关于Cron可以点击这里查看小七之前的博客。
需要注意的是,CronJob的时间以启动该CronJob任务的Master节点的时间为准。
Cronjob只负责创建与其计划相匹配的Job,而Job则负责管理它所代表的Pod。也就是说,CronJob只负责创建Job,具体的管理操作还是由Job来负责。
这里是CronJob的官方文档。
我们新建一个hellocronjob.yml来查看一下它的工作情况

apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: “*/1 * * * *”
jobTemplate:
spec:
template:
spec:
containers:
– name: hello
image: busybox
args:
– /bin/sh
– -c
– date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
这个定时任务的操作就是每分钟输出一次时间和Hello from the Kubernetes cluster。

查看cronjob的运行状态

kubectl get cronjobs.batch
kubectl get jobs.batch –watch

我们随便查看其中的一个log可以看到输出的结果:

基于redis的延迟消息队列设计 - peachyy - 博客园

mikel阅读(893)

来源: 基于redis的延迟消息队列设计 – peachyy – 博客园

需求背景

  • 用户下订单成功之后隔20分钟给用户发送上门服务通知短信
  • 订单完成一个小时之后通知用户对上门服务进行评价
  • 业务执行失败之后隔10分钟重试一次

    类似的场景比较多 简单的处理方式就是使用定时任务 假如数据比较多的时候 有的数据可能延迟比较严重,而且越来越多的定时业务导致任务调度很繁琐不好管理。

队列设计

目前可以考虑使用rabbitmq来满足需求 但是不打算使用,因为目前太多的业务使用了另外的MQ中间件。

开发前需要考虑的问题?

  • 及时性 消费端能按时收到
  • 同一时间消息的消费权重
  • 可靠性 消息不能出现没有被消费掉的情况
  • 可恢复 假如有其他情况 导致消息系统不可用了 至少能保证数据可以恢复
  • 可撤回 因为是延迟消息 没有到执行时间的消息支持可以取消消费
  • 高可用 多实例 这里指HA/主备模式并不是多实例同时一起工作
  • 消费端如何消费

    当然初步选用redis作为数据缓存的主要原因是因为redis自身支持zset的数据结构(score 延迟时间毫秒) 这样就少了排序的烦恼而且性能还很高,正好我们的需求就是按时间维度去判定执行的顺序 同时也支持map list数据结构。

简单定义一个消息数据结构

private String topic;/***topic**/
private String id;/***自动生成 全局惟一 snowflake**/
private String bizKey;
private long delay;/***延时毫秒数**/
private int priority;//优先级
private long ttl;/**消费端消费的ttl**/
private String body;/***消息体**/
private long createTime=System.currentTimeMillis();
private int status= Status.WaitPut.ordinal();

运行原理:

  1. Map来存储元数据。id作为key,整个消息结构序列化(json/…)之后作为value,放入元消息池中。
  2. id放入其中(有N个)一个zset有序列表中,以createTime+delay+priority作为score。修改状态为正在延迟中
  3. 使用timer实时监控zset有序列表中top 10的数据 。 如果数据score<=当前时间毫秒就取出来,根据topic重新放入一个新的可消费列表(list)中,在zset中删除已经取出来的数据,并修改状态为待消费
  4. 客户端获取数据只需要从可消费队列中获取就可以了。并且状态必须为待消费 运行时间需要<=当前时间的 如果不满足 重新放入zset列表中,修改状态为正在延迟。如果满足修改状态为已消费。或者直接删除元数据。

客户端

因为涉及到不同程序语言的问题,所以当前默认支持http访问方式。

  1. 添加延时消息添加成功之后返回消费唯一ID POST /push {…..消息体}
  2. 删除延时消息 需要传递消息ID GET /delete?id=
  3. 恢复延时消息 GET /reStore?expire=true|false expire是否恢复已过期未执行的消息。
  4. 恢复单个延时消息 需要传递消息ID GET /reStore/id
  5. 获取消息 需要长连接 GET /get/topic

用nginx暴露服务,配置为轮询 在添加延迟消息的时候就可以流量平均分配。

目前系统中客户端并没有采用HTTP长连接的方式来消费消息,而是采用MQ的方式来消费数据这样客户端就可以不用关心延迟消息队列。只需要在发送MQ的时候拦截一下 如果是延迟消息就用延迟消息系统处理。

消息可恢复

实现恢复的原理 正常情况下一般都是记录日志,比如mySQLbinlog等。

这里我们直接采用mySQL数据库作为记录日志。

目前打算创建以下2张表:

  1. 消息表 字段包括整个消息体
  2. 消息流转表 字段包括消息ID、变更状态、变更时间、zset扫描线程Name、host/ip

定义zset扫描线程Name是为了更清楚的看到消息被分发到具体哪个zset中。前提是zset的key和监控zset的线程名称要有点关系 这里也可以是zset key。

举个栗子

假如redis服务器宕机了,重启之后发现数据也没有了。所以这个恢复是很有必要的,只需要从表1也就是消息表中把消息状态不等于已消费的数据全部重新分发到延迟队列中去,然后同步一下状态就可以了。

当然恢复单个任务也可以这么干。

关于高可用

分布式协调还是选用zookeeper吧。

如果有多个实例最多同时只能有1个实例工作 这样就避免了分布式竞争锁带来的坏处,当然如果业务需要多个实例同时工作也是支持的,也就是一个消息最多只能有1个实例处理,可以选用zookeeper或者redis就能实现分布式锁了。

最终做了一下测试多实例同时运行,可能因为会涉及到锁的问题性能有所下降,反而单机效果很好。所以比较推荐基于docker的主备部署模式。

扩展

支持zset队列个数可配置 避免大数据带来高延迟的问题。

目前存在日志和redis元数据有可能不一致的问题 如mysql挂了,写日志不会成功。

设计图:

 

另外分享一个不完整简陋的开源版本  https://github.com/peachyy/sdmq.git  后期会进行模块拆分 优化

rabbitMQ实现延迟消息队列 - 简书

mikel阅读(3705)

来源: rabbitMQ实现延迟消息队列 – 简书

一、延迟消息适应场景

一般延迟队列用于特定事件发生后隔一段时间需要做特定处理的场景,下面举几个常见的栗子

1.电商系统中,若用户下单后30min不支付,自动取消订单
2.用户登录APP浏览特定商品20min后还没下单,自动推送商品评测信息的消息并发放商品相关优惠券

二、rabbitMQ的延迟消息

Rabbitmq本身是没有延迟队列的,要实现延迟消息,一般有两种方式:

1.通过Rabbitmq本身队列的特性来实现,需要使用Rabbitmq的死信交换机(Exchange)和消息的存活时间TTL(Time To Live)
2.在rabbitmq 3.5.7及以上的版本提供了一个插件(rabbitmq-delayed-message-exchange)来实现延迟队列功能。同时插件依赖Erlang/OPT 18.0及以上

一个个来看

三、rabbitMQ不使用插件实现延迟消息

3.1 原理图解

rabbitMQ延迟消息原理.png

若想不借助插件实现rabbitMQ的延迟消息,实际就是利用一个没有消费者的Queue1,等待消息过期后,通过交换机转发到Queue2来进行消费,消息的延迟时间就是消息在Queue1中的存活时间

3.2 在一个传统的spring项目中配置rabbitMQ的延迟消息

3.2.1 创建一个自动过期的消息队列(Queue1)

此队列没有消费类,可以通过x-message-ttl设置消息过期的时间,默认单位是毫秒,通过x-dead-letter-exchange设置死信后转发的交换机,通过x-dead-letter-routing-key来设置死信交换机中,真正需要转发的绑定的key,例如一个延迟5分钟的延迟队列的配置可以为:

 <rabbit:queue id="delayFiveMinuteQueue"  durable="true" auto-delete="false" exclusive="false" name="delayFiveMinuteQueue">
        <rabbit:queue-arguments>
            <!-- 设置延迟队列的过期时间 -->
            <entry key="x-message-ttl" value="300000" value-type="java.lang.Integer"/>
            <entry key="x-dead-letter-exchange" value="mq-exchange" />
            <entry key="x-dead-letter-routing-key" value="delayConsumerQueue" />
        </rabbit:queue-arguments>
</rabbit:queue>
3.2.2 创建转发后的消息队列(Queue2)

此队列为转发后的队列,故需要有消费类,其配置和一般的队列保持一致,例如

<!-- 延迟消息的消费队列 -->
<rabbit:queue id="delayConsumerQueue" durable="true" auto-delete="false" exclusive="false"  name="delayConsumerQueue"/>
3.2.3在交换机上进行绑定

交换机需要绑定队列和转发的key之间的关系,故需要注意的是Queue1的x-dead-letter-routing-key一定要和交换机中Queue2绑定的key保持一致,例如上述两个队列绑定后的配置示例为:

<rabbit:direct-exchange name="mq-exchange" durable="true" auto-delete="false" id="mq-exchange">
        <rabbit:bindings>
            <rabbit:binding queue="delayConsumerQueue" key="delayConsumerQueue" />
            <rabbit:binding queue="delayFiveMinuteQueue" key="delayFiveMinuteQueue" />
        </rabbit:bindings>
</rabbit:direct-exchange>

3.3不使用插件实现延迟消息的局限性

可以看到如果不使用插件,延迟消息的延迟时间是依赖于Queue1的x-message-ttl的,也就是说,需要支持多少种延迟的时间,就得提前设置好多少个无消费类的Queue,而且由于转发绑定的Queue2需要配到交换机中,比较死板,而真实的业务中消费类肯定是不一样的,故我真正在实现的时候在发到Queue1之前把消息体及需要消费的Queue(假设名称为Queue3)进行了落库,Queue2中进行消费时,实际是查库把消息读出来然后发送到了Queue3

而如果使用插件就没有以上的局限

四、rabbitMQ使用插件实现延迟消息

4.1 插件简介

在rabbitmq 3.5.7及以上的版本提供了一个插件(rabbitmq-delayed-message-exchange)来实现延迟队列功能。同时插件依赖Erlang/OPT 18.0及以上。

插件源码地址:
https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

插件下载地址:
https://bintray.com/rabbitmq/community-plugins/rabbitmq_delayed_message_exchange

4.2 插件使用方式

4.2.1 安装

进入插件安装目录

{rabbitmq-server}/plugins/(可以查看一下当前已存在的插件)

下载插件

wget https://bintray.com/rabbitmq/community-plugins/download_file?file_path=rabbitmq_delayed_message_exchange-0.0.1.ez

(如果下载的文件名称不规则就手动重命名一下如:rabbitmq_delayed_message_exchange-0.0.1.ez)

4.2.2 启用插件

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

(关闭插件)
rabbitmq-plugins disable rabbitmq_delayed_message_exchange

4.2.3 插件使用

在需要发送延迟消息队列的项目中,声明一个x-delayed-message类型的交换机来使用delayed-messaging特性,注意这个交换机并不是rabbitmq本身的,而是插件提供的,一定要是x-delayed-message类型,绑定的queue就是正常的queue即可,不需要额外多余的queue(这是和不用插件方式的最大区别及好处),例如

<rabbit:direct-exchange name="mq-exchange-delayed" durable="true" auto-delete="false" id="mq-exchange-delayed" delayed="true">
    <rabbit:bindings>
         <rabbit:binding queue="demoQueue" key="demoQueue"/>
    </rabbit:bindings>
</rabbit:direct-exchange>

消息发送时,在header添加”x-delay”参数来控制消息的延时时间,如果使用的是spring-rabbit中的RabbitTemplate,只需要通过messageProperties.setDelay(delay)方法set上延迟时间即可(单位为毫秒)

作者:周维的笔记
链接:https://www.jianshu.com/p/7e5f0742c8e3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Redis可以做哪些事? - Java旅途 - 博客园

mikel阅读(1002)

来源: Redis可以做哪些事? – Java旅途 – 博客园

Redis是一种基于键值对的NoSQL数据库,它的值主要由string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)五种基本数据结构构成,除此之外还支持一些其他的数据结构和算法。key都是由字符串构成的,那么这五种数据结构的使用场景有哪些?一起来看看!

一 字符串

字符串类型是Redis最基础的数据结构,字符串类型可以是JSONXML甚至是二进制的图片等数据,但是最大值不能超过512MB。

1.1 内部编码

Redis会根据当前值的类型和长度决定使用哪种内部编码来实现。

字符串类型的内部编码有3种:

  1. int:8个字节的长整型。
  2. embstr:小于等于39个字节的字符串。
  3. raw:大于39个字节的字符串。

1.2 使用场景

1.2.1 缓存

在web服务中,使用MySQL作为数据库,Redis作为缓存。由于Redis具有支撑高并发的特性,通常能起到加速读写和降低后端压力的作用。web端的大多数请求都是从Redis中获取的数据,如果Redis中没有需要的数据,则会从MySQL中去获取,并将获取到的数据写入redis。

1.2.2 计数

Redis中有一个字符串相关的命令incr keyincr命令对值做自增操作,返回结果分为以下三种情况:

  • 值不是整数,返回错误
  • 值是整数,返回自增后的结果
  • key不存在,默认键为0,返回1

比如文章的阅读量,视频的播放量等等都会使用redis来计数,每播放一次,对应的播放量就会加1,同时将这些数据异步存储到数据库中达到持久化的目的。

1.2.3 共享Session

在分布式系统中,用户的每次请求会访问到不同的服务器,这就会导致session不同步的问题,假如一个用来获取用户信息的请求落在A服务器上,获取到用户信息后存入session。下一个请求落在B服务器上,想要从session中获取用户信息就不能正常获取了,因为用户信息的session在服务器A上,为了解决这个问题,使用redis集中管理这些session,将session存入redis,使用的时候直接从redis中获取就可以了。

1.2.4 限速

为了安全考虑,有些网站会对IP进行限制,限制同一IP在一定时间内访问次数不能超过n次。

二 哈希

Redis中,哈希类型是指一个键值对的存储结构。

2.1 内部编码

哈希类型的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)同时所有值都小于hash-max-ziplist-value配置(默认64字节)时使用。ziplist使用更加紧凑的结构实现多个元素的连续存储,所以比hashtable更加节省内存。
  • hashtable(哈希表):当ziplist不能满足要求时,会使用hashtable。

2.2 使用场景

由于hash类型存储的是一个键值对,比如数据库有以下一个用户表结构

id name age
1 Java旅途 18

将以上信息存入redis,用表明:id作为key,用户属性作为值:

hset user:1 name Java旅途 age 18

使用哈希存储会比字符串更加方便直观

三 列表

列表类型用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素,列表的两端都可以插入和弹出元素。

3.1 内部编码

列表的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于list-max-ziplist-entries配置(默认512个)同时所有值都小于list-max-ziplist-value配置(默认64字节)时使用。ziplist使用更加紧凑的结构实现多个元素的连续存储,所以比hashtable更加节省内存。
  • linkedlist(链表):当ziplist不能满足要求时,会使用linkedlist。

3.2 使用场景

3.2.1 消息队列

列表用来存储多个有序的字符串,既然是有序的,那么就满足消息队列的特点。使用lpush+rpop或者rpush+lpop实现消息队列。除此之外,redis支持阻塞操作,在弹出元素的时候使用阻塞命令来实现阻塞队列。

3.2.2 栈

由于列表存储的是有序字符串,满足队列的特点,也就能满足栈先进后出的特点,使用lpush+lpop或者rpush+rpop实现栈。

3.2.3 文章列表

因为列表的元素不但是有序的,而且还支持按照索引范围获取元素。因此我们可以使用命令lrange key 0 9分页获取文章列表

四 集合

集合类型也可以保存多个字符串元素,与列表不同的是,集合中不允许有重复元素并且集合中的元素是无序的。一个集合最多可以存储2^32-1个元素。

4.1 内部编码

集合类型的内部编码有两种:

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,redis会选用intset来作为集合的内部实现,从而减少内存的使用。
  • hashtable(哈希表):当intset不能满足要求时,会使用hashtable。

4.2 使用场景

4.2.1 用户标签

例如一个用户对篮球、足球感兴趣,另一个用户对橄榄球、乒乓球感兴趣,这些兴趣点就是一个标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同感兴趣的标签。给用户打标签的时候需要①给用户打标签,②给标签加用户,需要给这两个操作增加事务。

  • 给用户打标签
sadd user:1:tags tag1 tag2
  • 给标签添加用户
sadd tag1:users user:1

sadd tag2:users user:1

使用交集(sinter)求两个user的共同标签

sinter user:1:tags user:2:tags

4.2.2 抽奖功能

集合有两个命令支持获取随机数,分别是:

  • 随机获取count个元素,集合元素个数不变

srandmember key [count]

  • 随机弹出count个元素,元素从集合弹出,集合元素个数改变

spop key [count]

用户点击抽奖按钮,参数抽奖,将用户编号放入集合,然后抽奖,分别抽一等奖、二等奖,如果已经抽中一等奖的用户不能参数抽二等奖则使用spop,反之使用srandmember

五 有序集合

有序集合和集合一样,不能有重复元素。但是可以排序,它给每个元素设置一个score作为排序的依据。最多可以存储2^32-1个元素。

5.1 内部编码

有序集合类型的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数小于list-max-ziplist-entries配置(默认128个)同时所有值都小于list-max-ziplist-value配置(默认64字节)时使用。ziplist使用更加紧凑的结构实现多个元素的连续存储,更加节省内存。
  • skiplist(跳跃表):当不满足ziplist的要求时,会使用skiplist。

5.2 使用场景

5.2.1 排行榜

用户发布了n篇文章,其他人看到文章后给喜欢的文章点赞,使用score来记录点赞数,有序集合会根据score排行。流程如下

用户发布一篇文章,初始点赞数为0,即score为0

zadd user:article 0 a

有人给文章a点赞,递增1

zincrby user:article 1 a

查询点赞前三篇文章

zrevrangebyscore user:article 0 2

查询点赞后三篇文章

zrangebyscore user:article 0 2

5.2.2 延迟消息队列

下单系统,下单后需要在15分钟内进行支付,如果15分钟未支付则自动取消订单。将下单后的十五分钟后时间作为score,订单作为value存入redis,消费者轮询去消费,如果消费的大于等于这笔记录的score,则将这笔记录移除队列,取消订单。

总结

在开发中,字符串类型是用的最多的数据类型,导致我们忽视了redis的其他四种数据类型,在具体场景下选择具体的数据类型对提升redis性能有非常大的帮助。redis虽然支持消息队列的实现,但是并不支持ack。所以redis实现的消息队列不能保证消息的可靠性,除非自己实现消息确认机制,不过这非常麻烦,所以如果是重要的消息还是推荐使用专门的消息队列去做。

怎么实现员工和工资大数据分析,echarts+js实现 - 气宇轩昂_2017 - 博客园

mikel阅读(1220)

来源: 怎么实现员工和工资大数据分析,echarts+js实现 – 气宇轩昂_2017 – 博客园

前言

现如今市场上的人事系统五花八门,可做了大数据分析的人事系统少之又少,最近本人花了一个星期好好研究了大数据展示方面的内容,图表主要用的是echarts来实现的,官网地址:https://echarts.apache.org/zh/index.html

下面两张图片展示出我实现的员工和工资的大数据分析界面:

员工大数据分析中心

工资大数据分析中心

如何实现漂亮的图表

地图实现

首先引入js文件,去官网可以下载到这几个文件,引入到项目中就好了。

<script type="text/javascript" src="~/showdata/js/jquery.js"></script>
<script type="text/javascript" src="~/showdata/js/echarts.min.js"></script>
<script type="text/javascript" src="~/showdata/js/china.js"></script>
<script type="text/javascript" src="~/showdata/js/area_echarts_hr.js">

图层引入

<div class="map4" id="map_1"></div>

地图实现的js

$(function () {
map();
})
var lightProvince = [];
var provinceMap = {
台湾: ‘taiwan’,
河北: ‘hebei’,
山西: ‘shanxi’,
辽宁: ‘liaoning’,
吉林: ‘jilin’,
黑龙江: ‘heilongjiang’,
江苏: ‘jiangsu’,
浙江: ‘zhejiang’,
安徽: ‘anhui’,
福建: ‘fujian’,
江西: ‘jiangxi’,
山东: ‘shandong’,
河南: ‘henan’,
湖北: ‘hubei’,
湖南: ‘hunan’,
广东: ‘guangdong’,
海南: ‘hainan’,
四川: ‘sichuan’,
贵州: ‘guizhou’,
云南: ‘yunnan’,
陕西: ‘shanxi1’,
甘肃: ‘gansu’,
青海: ‘qinghai’,
新疆: ‘xinjiang’,
广西: ‘guangxi’,
内蒙古: ‘neimenggu’,
宁夏: ‘ningxia’,
西藏: ‘xizang’,
北京: ‘beijing’,
天津: ‘tianjin’,
上海: ‘shanghai’,
重庆: ‘chongqing’,
香港: ‘xianggang’,
澳门: ‘aomen’
};
var py_provinceMap = {
china: ‘全国’,
hebei: ‘河北’,
shanxi: ‘山西’,
liaoning: ‘辽宁’,
jilin: ‘吉林’,
heilongjiang: ‘黑龙江’,
jiangsu: ‘江苏’,
zhejiang: ‘浙江’,
anhui: ‘安徽’,
fujian: ‘福建’,
jiangxi: ‘江西’,
shandong: ‘山东’,
henan: ‘河南’,
hubei: ‘湖北’,
hunan: ‘湖南’,
guangdong: ‘广东’,
hainan: ‘海南’,
sichuan: ‘四川’,
guizhou: ‘贵州’,
yunnan: ‘云南’,
shanxi: ‘陕西’,
gansu: ‘甘肃’,
qinghai: ‘青海’,
xinjiang: ‘新疆’,
guangxi: ‘广西’,
neimenggu: ‘内蒙古’,
ningxia: ‘宁夏’,
xizang: ‘西藏’,
beijing: ‘北京’,
tianjin: ‘天津’,
shanghai: ‘上海’,
chongqing: ‘重庆’
};
var cityMap = {
北京市: ‘110100’,
天津市: ‘120100’,
上海市: ‘310100’,
重庆市: ‘500100’,
崇明县: ‘310200’,
湖北省直辖县市: ‘429000’,
铜仁市: ‘522200’,
毕节市: ‘522400’,
石家庄市: ‘130100’,
唐山市: ‘130200’,
秦皇岛市: ‘130300’,
邯郸市: ‘130400’,
邢台市: ‘130500’,
保定市: ‘130600’,
张家口市: ‘130700’,
承德市: ‘130800’,
沧州市: ‘130900’,
廊坊市: ‘131000’,
衡水市: ‘131100’,
太原市: ‘140100’,
大同市: ‘140200’,
阳泉市: ‘140300’,
长治市: ‘140400’,
晋城市: ‘140500’,
朔州市: ‘140600’,
晋中市: ‘140700’,
运城市: ‘140800’,
忻州市: ‘140900’,
临汾市: ‘141000’,
吕梁市: ‘141100’,
呼和浩特市: ‘150100’,
包头市: ‘150200’,
乌海市: ‘150300’,
赤峰市: ‘150400’,
通辽市: ‘150500’,
鄂尔多斯市: ‘150600’,
呼伦贝尔市: ‘150700’,
巴彦淖尔市: ‘150800’,
乌兰察布市: ‘150900’,
兴安盟: ‘152200’,
锡林郭勒盟: ‘152500’,
阿拉善盟: ‘152900’,
沈阳市: ‘210100’,
大连市: ‘210200’,
鞍山市: ‘210300’,
抚顺市: ‘210400’,
本溪市: ‘210500’,
丹东市: ‘210600’,
锦州市: ‘210700’,
营口市: ‘210800’,
阜新市: ‘210900’,
辽阳市: ‘211000’,
盘锦市: ‘211100’,
铁岭市: ‘211200’,
朝阳市: ‘211300’,
葫芦岛市: ‘211400’,
长春市: ‘220100’,
吉林市: ‘220200’,
四平市: ‘220300’,
辽源市: ‘220400’,
通化市: ‘220500’,
白山市: ‘220600’,
松原市: ‘220700’,
白城市: ‘220800’,
延边朝鲜族自治州: ‘222400’,
哈尔滨市: ‘230100’,
齐齐哈尔市: ‘230200’,
鸡西市: ‘230300’,
鹤岗市: ‘230400’,
双鸭山市: ‘230500’,
大庆市: ‘230600’,
伊春市: ‘230700’,
佳木斯市: ‘230800’,
七台河市: ‘230900’,
牡丹江市: ‘231000’,
黑河市: ‘231100’,
绥化市: ‘231200’,
大兴安岭地区: ‘232700’,
南京市: ‘320100’,
无锡市: ‘320200’,
徐州市: ‘320300’,
常州市: ‘320400’,
苏州市: ‘320500’,
南通市: ‘320600’,
连云港市: ‘320700’,
淮安市: ‘320800’,
盐城市: ‘320900’,
扬州市: ‘321000’,
镇江市: ‘321100’,
泰州市: ‘321200’,
宿迁市: ‘321300’,
杭州市: ‘330100’,
宁波市: ‘330200’,
温州市: ‘330300’,
嘉兴市: ‘330400’,
湖州市: ‘330500’,
绍兴市: ‘330600’,
金华市: ‘330700’,
衢州市: ‘330800’,
舟山市: ‘330900’,
台州市: ‘331000’,
丽水市: ‘331100’,
合肥市: ‘340100’,
芜湖市: ‘340200’,
蚌埠市: ‘340300’,
淮南市: ‘340400’,
马鞍山市: ‘340500’,
淮北市: ‘340600’,
铜陵市: ‘340700’,
安庆市: ‘340800’,
黄山市: ‘341000’,
滁州市: ‘341100’,
阜阳市: ‘341200’,
宿州市: ‘341300’,
六安市: ‘341500’,
亳州市: ‘341600’,
池州市: ‘341700’,
宣城市: ‘341800’,
福州市: ‘350100’,
厦门市: ‘350200’,
莆田市: ‘350300’,
三明市: ‘350400’,
泉州市: ‘350500’,
漳州市: ‘350600’,
南平市: ‘350700’,
龙岩市: ‘350800’,
宁德市: ‘350900’,
南昌市: ‘360100’,
景德镇市: ‘360200’,
萍乡市: ‘360300’,
九江市: ‘360400’,
新余市: ‘360500’,
鹰潭市: ‘360600’,
赣州市: ‘360700’,
吉安市: ‘360800’,
宜春市: ‘360900’,
抚州市: ‘361000’,
上饶市: ‘361100’,
济南市: ‘370100’,
青岛市: ‘370200’,
淄博市: ‘370300’,
枣庄市: ‘370400’,
东营市: ‘370500’,
烟台市: ‘370600’,
潍坊市: ‘370700’,
济宁市: ‘370800’,
泰安市: ‘370900’,
威海市: ‘371000’,
日照市: ‘371100’,
莱芜市: ‘371200’,
临沂市: ‘371300’,
德州市: ‘371400’,
聊城市: ‘371500’,
滨州市: ‘371600’,
菏泽市: ‘371700’,
郑州市: ‘410100’,
开封市: ‘410200’,
洛阳市: ‘410300’,
平顶山市: ‘410400’,
安阳市: ‘410500’,
鹤壁市: ‘410600’,
新乡市: ‘410700’,
焦作市: ‘410800’,
濮阳市: ‘410900’,
许昌市: ‘411000’,
漯河市: ‘411100’,
三门峡市: ‘411200’,
南阳市: ‘411300’,
商丘市: ‘411400’,
信阳市: ‘411500’,
周口市: ‘411600’,
驻马店市: ‘411700’,
省直辖县级行政区划: ‘469000’,
武汉市: ‘420100’,
黄石市: ‘420200’,
十堰市: ‘420300’,
宜昌市: ‘420500’,
襄阳市: ‘420600’,
鄂州市: ‘420700’,
荆门市: ‘420800’,
孝感市: ‘420900’,
荆州市: ‘421000’,
黄冈市: ‘421100’,
咸宁市: ‘421200’,
随州市: ‘421300’,
恩施土家族苗族自治州: ‘422800’,
长沙市: ‘430100’,
株洲市: ‘430200’,
湘潭市: ‘430300’,
衡阳市: ‘430400’,
邵阳市: ‘430500’,
岳阳市: ‘430600’,
常德市: ‘430700’,
张家界市: ‘430800’,
益阳市: ‘430900’,
郴州市: ‘431000’,
永州市: ‘431100’,
怀化市: ‘431200’,
娄底市: ‘431300’,
湘西土家族苗族自治州: ‘433100’,
广州市: ‘440100’,
韶关市: ‘440200’,
深圳市: ‘440300’,
珠海市: ‘440400’,
汕头市: ‘440500’,
佛山市: ‘440600’,
江门市: ‘440700’,
湛江市: ‘440800’,
茂名市: ‘440900’,
肇庆市: ‘441200’,
惠州市: ‘441300’,
梅州市: ‘441400’,
汕尾市: ‘441500’,
河源市: ‘441600’,
阳江市: ‘441700’,
清远市: ‘441800’,
东莞市: ‘441900’,
中山市: ‘442000’,
潮州市: ‘445100’,
揭阳市: ‘445200’,
云浮市: ‘445300’,
南宁市: ‘450100’,
柳州市: ‘450200’,
桂林市: ‘450300’,
梧州市: ‘450400’,
北海市: ‘450500’,
防城港市: ‘450600’,
钦州市: ‘450700’,
贵港市: ‘450800’,
玉林市: ‘450900’,
百色市: ‘451000’,
贺州市: ‘451100’,
河池市: ‘451200’,
来宾市: ‘451300’,
崇左市: ‘451400’,
海口市: ‘460100’,
三亚市: ‘460200’,
三沙市: ‘460300’,
成都市: ‘510100’,
自贡市: ‘510300’,
攀枝花市: ‘510400’,
泸州市: ‘510500’,
德阳市: ‘510600’,
绵阳市: ‘510700’,
广元市: ‘510800’,
遂宁市: ‘510900’,
内江市: ‘511000’,
乐山市: ‘511100’,
南充市: ‘511300’,
眉山市: ‘511400’,
宜宾市: ‘511500’,
广安市: ‘511600’,
达州市: ‘511700’,
雅安市: ‘511800’,
巴中市: ‘511900’,
资阳市: ‘512000’,
阿坝藏族羌族自治州: ‘513200’,
甘孜藏族自治州: ‘513300’,
凉山彝族自治州: ‘513400’,
贵阳市: ‘520100’,
六盘水市: ‘520200’,
遵义市: ‘520300’,
安顺市: ‘520400’,
黔西南布依族苗族自治州: ‘522300’,
黔东南苗族侗族自治州: ‘522600’,
黔南布依族苗族自治州: ‘522700’,
昆明市: ‘530100’,
曲靖市: ‘530300’,
玉溪市: ‘530400’,
保山市: ‘530500’,
昭通市: ‘530600’,
丽江市: ‘530700’,
普洱市: ‘530800’,
临沧市: ‘530900’,
楚雄彝族自治州: ‘532300’,
红河哈尼族彝族自治州: ‘532500’,
文山壮族苗族自治州: ‘532600’,
西双版纳傣族自治州: ‘532800’,
大理白族自治州: ‘532900’,
德宏傣族景颇族自治州: ‘533100’,
怒江傈僳族自治州: ‘533300’,
迪庆藏族自治州: ‘533400’,
拉萨市: ‘540100’,
昌都地区: ‘542100’,
山南地区: ‘542200’,
日喀则地区: ‘542300’,
那曲地区: ‘542400’,
阿里地区: ‘542500’,
林芝地区: ‘542600’,
西安市: ‘610100’,
铜川市: ‘610200’,
宝鸡市: ‘610300’,
咸阳市: ‘610400’,
渭南市: ‘610500’,
延安市: ‘610600’,
汉中市: ‘610700’,
榆林市: ‘610800’,
安康市: ‘610900’,
商洛市: ‘611000’,
兰州市: ‘620100’,
嘉峪关市: ‘620200’,
金昌市: ‘620300’,
白银市: ‘620400’,
天水市: ‘620500’,
武威市: ‘620600’,
张掖市: ‘620700’,
平凉市: ‘620800’,
酒泉市: ‘620900’,
庆阳市: ‘621000’,
定西市: ‘621100’,
陇南市: ‘621200’,
临夏回族自治州: ‘622900’,
甘南藏族自治州: ‘623000’,
西宁市: ‘630100’,
海东地区: ‘632100’,
海北藏族自治州: ‘632200’,
黄南藏族自治州: ‘632300’,
海南藏族自治州: ‘632500’,
果洛藏族自治州: ‘632600’,
玉树藏族自治州: ‘632700’,
海西蒙古族藏族自治州: ‘632800’,
银川市: ‘640100’,
石嘴山市: ‘640200’,
吴忠市: ‘640300’,
固原市: ‘640400’,
中卫市: ‘640500’,
乌鲁木齐市: ‘650100’,
克拉玛依市: ‘650200’,
吐鲁番地区: ‘652100’,
哈密地区: ‘652200’,
昌吉回族自治州: ‘652300’,
博尔塔拉蒙古自治州: ‘652700’,
巴音郭楞蒙古自治州: ‘652800’,
阿克苏地区: ‘652900’,
克孜勒苏柯尔克孜自治州: ‘653000’,
喀什地区: ‘653100’,
和田地区: ‘653200’,
伊犁哈萨克自治州: ‘654000’,
塔城地区: ‘654200’,
阿勒泰地区: ‘654300’,
自治区直辖县级行政区划: ‘659000’,
台湾省: ‘710000’,
香港特别行政区: ‘810100’,
澳门特别行政区: ‘820000’
};

var lightColor = {
河北: ‘#ffa259’,
山西: ‘#fe6845’,
辽宁: ‘#fa4252’,
吉林: ‘#3fc5f0’,
黑龙江: ‘#c72c41’,
江苏: ‘#f4efd3’,
浙江: ‘#c72c41’,
安徽: ‘#e13a9d’,
福建: ‘#e13a9d’,
江西: ‘#cf56a1’,
山东: ‘#fa697c’,
河南: ‘#a3f7bf’,
湖北: ‘#3ed4ff’,
湖南: ‘#9dab86’,
广东: ‘#a6c84c’,
海南: ‘#ffa259’,
四川: ‘#cfb495’,
贵州: ‘#f09595’,
云南: ‘#4f98ca’,
陕西: ‘#617be3’,
甘肃: ‘#9aceff’,
青海: ‘#9aceff’,
新疆: ‘#02a8a8’,
广西: ‘#fbdff0’,
内蒙古: ‘#42e6a4’,
宁夏: ‘#02a8a8’,
西藏: ‘#f6c3e5’,
北京: ‘#a278b5’,
天津: ‘#d6e5fa’,
上海: ‘#fbe3b9’,
重庆: ‘#bac7a7’
};

var geoCoordMap = {
‘新疆’: [86.22, 44.30],
‘九江’: [116.00, 29.70],
‘新乡’: [116.402217, 35.311657],
‘ ‘: [79.92, 37.12],
‘ ‘: [86.85, 47.70],
‘若羌县’: [88.17, 39.02],
‘上海’: [121.4648, 31.2891],
‘东莞’: [113.8953, 22.901],
‘东营’: [118.7073, 37.5513],
‘中山’: [113.4229, 22.478],
‘临汾’: [111.4783, 36.1615],
‘临沂’: [118.3118, 35.2936],
‘丹东’: [124.541, 40.4242],
‘丽水’: [119.5642, 28.1854],
‘乌鲁木齐’: [87.9236, 43.5883],
‘佛山’: [112.8955, 23.1097],
‘保定’: [115.0488, 39.0948],
‘兰州’: [103.5901, 36.3043],
‘包头’: [110.3467, 41.4899],
‘北京’: [116.4551, 40.2539],
‘北海’: [109.314, 21.6211],
‘南京’: [118.8062, 31.9208],
‘南宁’: [108.479, 23.1152],
‘南昌’: [116.0046, 28.6633],
‘南通’: [121.1023, 32.1625],
‘厦门’: [118.1689, 24.6478],
‘台州’: [121.1353, 28.6688],
‘合肥’: [117.29, 32.0581],
‘呼和浩特’: [111.4124, 40.4901],
‘咸阳’: [108.4131, 34.8706],
‘哈尔滨’: [127.9688, 45.368],
‘唐山’: [118.4766, 39.6826],
‘嘉兴’: [120.9155, 30.6354],
‘大同’: [113.7854, 39.8035],
‘大连’: [122.2229, 39.4409],
‘天津’: [117.4219, 39.4189],
‘太原’: [112.3352, 37.9413],
‘威海’: [121.9482, 37.1393],
‘宁波’: [121.5967, 29.6466],
‘宝鸡’: [107.1826, 34.3433],
‘宿迁’: [118.5535, 33.7775],
‘常州’: [119.4543, 31.5582],
‘广州’: [113.5107, 23.2196],
‘廊坊’: [116.521, 39.0509],
‘延安’: [109.1052, 36.4252],
‘张家口’: [115.1477, 40.8527],
‘徐州’: [117.5208, 34.3268],
‘德州’: [116.6858, 37.2107],
‘惠州’: [114.6204, 23.1647],
‘成都’: [103.9526, 30.7617],
‘扬州’: [119.4653, 32.8162],
‘承德’: [117.5757, 41.4075],
‘拉萨’: [91.1865, 30.1465],
‘无锡’: [120.3442, 31.5527],
‘日照’: [119.2786, 35.5023],
‘昆明’: [102.9199, 25.4663],
‘杭州’: [119.5313, 29.8773],
‘枣庄’: [117.323, 34.8926],
‘柳州’: [109.3799, 24.9774],
‘株洲’: [113.5327, 27.0319],
‘武汉’: [114.3896, 30.6628],
‘汕头’: [117.1692, 23.3405],
‘江门’: [112.6318, 22.1484],
‘沈阳’: [123.1238, 42.1216],
‘沧州’: [116.8286, 38.2104],
‘河源’: [114.917, 23.9722],
‘泉州’: [118.3228, 25.1147],
‘泰安’: [117.0264, 36.0516],
‘泰州’: [120.0586, 32.5525],
‘济南’: [117.1582, 36.8701],
‘济宁’: [116.8286, 35.3375],
‘海口’: [110.3893, 19.8516],
‘淄博’: [118.0371, 36.6064],
‘淮安’: [118.927, 33.4039],
‘深圳’: [114.5435, 22.5439],
‘清远’: [112.9175, 24.3292],
‘温州’: [120.498, 27.8119],
‘渭南’: [109.7864, 35.0299],
‘湖州’: [119.8608, 30.7782],
‘湘潭’: [112.5439, 27.7075],
‘滨州’: [117.8174, 37.4963],
‘潍坊’: [119.0918, 36.524],
‘烟台’: [120.7397, 37.5128],
‘玉溪’: [101.9312, 23.8898],
‘珠海’: [113.7305, 22.1155],
‘盐城’: [120.2234, 33.5577],
‘盘锦’: [121.9482, 41.0449],
‘石家庄’: [114.4995, 38.1006],
‘福州’: [119.4543, 25.9222],
‘秦皇岛’: [119.2126, 40.0232],
‘绍兴’: [120.564, 29.7565],
‘聊城’: [115.9167, 36.4032],
‘肇庆’: [112.1265, 23.5822],
‘舟山’: [122.2559, 30.2234],
‘苏州’: [120.6519, 31.3989],
‘莱芜’: [117.6526, 36.2714],
‘菏泽’: [115.6201, 35.2057],
‘营口’: [122.4316, 40.4297],
‘葫芦岛’: [120.1575, 40.578],
‘衡水’: [115.8838, 37.7161],
‘衢州’: [118.6853, 28.8666],
‘西宁’: [101.4038, 36.8207],
‘西安’: [109.1162, 34.2004],
‘贵阳’: [106.6992, 26.7682],
‘连云港’: [119.1248, 34.552],
‘邢台’: [114.8071, 37.2821],
‘邯郸’: [114.4775, 36.535],
‘郑州’: [113.4668, 34.6234],
‘鄂尔多斯’: [108.9734, 39.2487],
‘重庆’: [107.7539, 30.1904],
‘金华’: [120.0037, 29.1028],
‘铜川’: [109.0393, 35.1947],
‘银川’: [106.3586, 38.1775],
‘镇江’: [119.4763, 31.9702],
‘长春’: [125.8154, 44.2584],
‘长沙’: [113.0823, 28.2568],
‘长治’: [112.8625, 36.4746],
‘阳泉’: [113.4778, 38.0951],
‘青岛’: [120.4651, 36.3373],
‘韶关’: [113.7964, 24.7028]
};

var provinces = [‘shanghai’, ‘hebei’, ‘shanxi’, ‘neimenggu’, ‘liaoning’, ‘jilin’, ‘heilongjiang’, ‘jiangsu’, ‘zhejiang’, ‘anhui’, ‘fujian’, ‘jiangxi’, ‘shandong’, ‘henan’, ‘hubei’, ‘hunan’, ‘guangdong’, ‘guangxi’, ‘hainan’, ‘sichuan’, ‘guizhou’, ‘yunnan’, ‘xizang’, ‘shanxi1’, ‘gansu’, ‘qinghai’, ‘ningxia’, ‘xinjiang’, ‘beijing’, ‘tianjin’, ‘chongqing’, ‘xianggang’, ‘aomen’, ‘taiwan’]
var provincesText = [‘上海’, ‘河北’, ‘山西’, ‘内蒙古’, ‘辽宁’, ‘吉林’, ‘黑龙江’, ‘江苏’, ‘浙江’, ‘安徽’, ‘福建’, ‘江西’, ‘山东’, ‘河南’, ‘湖北’, ‘湖南’, ‘广东’, ‘广西’, ‘海南’, ‘四川’, ‘贵州’, ‘云南’, ‘西藏’, ‘陕西’, ‘甘肃’, ‘青海’, ‘宁夏’, ‘新疆’, ‘北京’, ‘天津’, ‘重庆’, ‘香港’, ‘澳门’, ‘台湾’]
var planePath = ‘path://M.6,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705’;
var convertData = function (data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var dataItem = data[i];
var fromCoord = geoCoordMap[dataItem.fromName];
var toCoord = geoCoordMap[dataItem.toName];
if (fromCoord && toCoord) {
res.push({
fromName: dataItem.fromName,
toName: dataItem.toName,
coords: [fromCoord, toCoord]
});
}
}
return res;
};
function SetMap() {
var url = ‘../showdata/mapset.html’;
$(“#iframeWin”).show();
//var name = ‘add’;
//var iWidth = 500;
//var iHeight = 500;
////获得窗口的垂直位置
//var iTop = (window.screen.availHeight – 30 – iHeight) / 2;
////获得窗口的水平位置
//var iLeft = (window.screen.availWidth – 10 – iWidth) / 2;
//window.open(url, name, ‘height=’ + iHeight + ‘,,innerHeight=’ + iHeight + ‘,width=’ + iWidth + ‘,innerWidth=’ + iWidth + ‘,top=’ + iTop + ‘,left=’ + iLeft + ‘,status=no,toolbar=no,menubar=no,location=no,resizable=no,scrollbars=0,titlebar=no’);
};
var series = [];
var mapSelected = ‘china’;
var showMapText = false;
var count = 0;
var seriesIndex;
var dataLength;
var dataIndex;
var timeTicket;
var geoData = {};
var mapJsonData;
var color = [‘#ffa259’, ‘#fe6845’, ‘#fa4252’, ‘#3fc5f0’, ‘#c72c41’, ‘#f4efd3’, ‘#c72c41’, ‘#e13a9d’, ‘#e13a9d’, ‘#ffd800’, ‘#51eaea’, ‘#a3f7bf’, ‘#3ed4ff’, ‘#ffa022’, ‘#a6c84c’];
var chartOption = {
title: {
show: true,
text: ‘实时货量流向图’,
target: null,
subtext: ‘地图设置’,
//sublink: ‘../showdata/mapset.html’,
sublink: ‘JavaScript:SetMap();’,
subtarget: ‘self’,
x: ‘center’,
y: ‘top’,
textAlign: null,
backgroundColor: ‘rgba(0,0,0,0)’,
borderColor: ‘#ccc’,
borderWidth: 0,
padding: 5,
itemGap: 10,
textStyle: {
fontSize: 30,
fontStyle: ‘normal’,
fontWeight: ‘normal’,
color: ‘white’
}
},
tooltip: {
trigger: ‘item’,
},
legend: {
orient: ‘vertical’,
top: ‘bottom’,
left: ‘right’,
data: [],
textStyle: {
color: ‘#fff’
},
selectedMode: ‘single’
},
geo: {
label: {
normal: {
show: showMapText,
color: ‘white’,
fontSize: 13
},
emphasis: {
show: false
}
},
roam: false,
layoutCenter: [‘50%’, ‘50%’],
layoutSize: “110%”,
itemStyle: {
normal: {
areaColor: ‘#101f32’,
borderColor: ‘#43d0d6’
},
emphasis: {
areaColor: ‘#617be3’
}
},
regions: []
},
series: []
};

var isProvince = function (name) {
return provincesText.some(function (province) {
return province === name
})
};
var isCity = function (name) {
var cityvalue = cityMap[name];
if (cityvalue != undefined) {
return true;
}
return false;
};
var facheList = function () {
series.push({
type: ‘lines’,
zlevel: 12,
effect: {
show: true,
period: 3,
trailLength: 0.7,
color: ‘#fff’,
symbol: ‘arrow’,
symbolSize: 8,
xAxisIndex: 0,
yAxisIndex: 0,
polarIndex: 0,
geoIndex: 0,
calendarIndex: 0,
},
lineStyle: {
normal: {
color: ‘white’,
width: 1,
opacity: 0.8,
curveness: 0.2
}
},
data: convertData(geoData.facheList)
}, {
type: ‘lines’,
zlevel: 12,
effect: {
show: true,
period: 3,
trailLength: 0,
symbol: planePath,
symbolSize: 8
},
lineStyle: {
normal: {
color: ‘#9b45e4’,
width: 1,
opacity: 0.6,
curveness: 0.2
}
},
data: convertData(geoData.facheList)
});

playFaCheList(geoData.facheList);
};
var cityList = function () {
for (let i = 0; i < geoData.cityList.length; i++) {
var ci = geoData.cityList[i];
series.push({
name: ci.name,
type: ‘effectScatter’,
coordinateSystem: ‘geo’,
zlevel: 2,
rippleEffect: {
period: 4,
scale: 2.5,
brushType: ‘stroke’
},
label: {
normal: {
//show: true,
position: ‘right’,
offset: [1, 0],
formatter: ‘{b}’
}
},
symbolSize: 10,
itemStyle: {
normal: {
color: ‘#00ffff’
},
emphasis: {
color: ‘green’
}
},
tooltip: {
trigger: ‘item’,
padding: 0,
enterable: true,
transitionDuration: 1,
textStyle: {
color: ‘#000’,
decoration: ‘none’,
},
showDelay: 2,
formatter: function (params) {
var tipHtml = ”;
tipHtml = ‘<div style=”width:200px;height:120px;background:rgba(22,80,158,0.8);border:1px solid rgba(7,166,255,0.7)”>’
+ ‘<div style=”width:100%;height:40px;line-height:40px;border-bottom:2px solid rgba(7,166,255,0.7);padding:0 20px”>’ + ‘<i style=”display:inline-block;width:8px;height:8px;background:#16d6ff;border-radius:40px;”>’ + ‘</i>’
+ ‘<span style=”margin-left:10px;color:#fff;font-size:16px;”>’ + params.name + ‘</span>’ + ‘</div>’
+ ‘<div style=”padding:10px”>’
+ ‘<p style=”color:#fff;font-size:15px;”>发货库存:’ + params.data.fhkc + ‘</p>’
+ ‘<p style=”color:#fff;font-size:15px;”>在途库存:’ + params.data.ztkc + ‘</p>’
+ ‘<p style=”color:#fff;font-size:15px;”>到货库存:’ + params.data.dhkc + ‘</p>’
+ ‘</div>’ + ‘</div>’;
return tipHtml;
},
axisPointer: {
show: true,
type: ‘cross’,
lineStyle: {
type: ‘dashed’,
width: 1
}
}
},
data: [{
name: ci.name,
value: geoCoordMap[ci.name],
fhkc: ci.fhkc,
ztkc: ci.ztkc,
dhkc: ci.dhkc
}]
});
};
};
var playList = function () {
series.push({
name: ”,
type: ‘effectScatter’,
coordinateSystem: ‘geo’,
zlevel: -2,
rippleEffect: {
period: 0,
scale: 0,
brushType: ‘stroke’
},
label: {
normal: {
show: false,
position: ‘right’,
offset: [5, 0],
formatter: ‘{b}’
}
},
symbolSize: 0,
itemStyle: {
normal: {
color: ‘#ffa259’
},
emphasis: {
color: ‘green’
}
},
tooltip: {
trigger: ‘item’,
padding: 0,
enterable: true,
transitionDuration: 1,
textStyle: {
color: ‘#000’,
decoration: ‘none’,
},
showDelay: 2,
formatter: function (params) {
var tipHtml = ”;
tipHtml = ‘<div style=”width:200px;height:120px;background:rgba(22,80,158,0.8);border:1px solid rgba(7,166,255,0.7)”>’
+ ‘<div style=”width:100%;height:40px;line-height:40px;border-bottom:2px solid rgba(7,166,255,0.7);padding:0 20px”>’ + ‘<i style=”display:inline-block;width:8px;height:8px;background:#16d6ff;border-radius:40px;”>’ + ‘</i>’
+ ‘<span style=”margin-left:10px;color:#fff;font-size:16px;”>’ + params.name + ‘</span>’ + ‘</div>’
+ ‘<div style=”padding:10px”>’
+ ‘<p style=”color:#fff;font-size:15px;”>发货库存:’ + params.data.fhkc + ‘</p>’
+ ‘<p style=”color:#fff;font-size:15px;”>在途库存:’ + params.data.ztkc + ‘</p>’
+ ‘<p style=”color:#fff;font-size:15px;”>到货库存:’ + params.data.dhkc + ‘</p>’
+ ‘</div>’ + ‘</div>’;
return tipHtml;
},
axisPointer: {
show: true,
type: ‘cross’,
lineStyle: {
type: ‘dashed’,
width: 1
}
}
},
data: geoData.cityList.map(function (ci) {
return {
name: ci.name,
value: geoCoordMap[ci.name],
fhkc: ci.fhkc,
ztkc: ci.ztkc,
dhkc: ci.dhkc
};
})
});
};

var seriesData = function () {

series = [];

facheList();

cityList();

//playList();

count = 0;
timeTicket = null;
seriesIndex = series.length – 1;
dataLength = series[seriesIndex].data.length;
dataIndex = 0;

chartOption.series = series;
};

var loadMap = function (param) {
var url = “../showdata/js/map/” + param + “.js”;
$.ajax({
url: url,
dataType: “json”
}).done(function (data) {
mapJsonData = data;
echarts.registerMap(param, mapJsonData);

if (param != ‘china’ && param != ‘全国’) {
showMapText = true;
}
else {
showMapText = false;
}

chartOption.geo.map = param;
chartOption.geo.label.normal.show = showMapText;

for (let i = 0; i < lightProvince.length; i++) {
let city = lightProvince[i];
chartOption.geo.regions.push({
name: city,
itemStyle: {
normal: {
areaColor: lightColor[city]
}
},
label: {
normal: {
show: true
},
emphasis: {
show: true
},
}
});
}
myMapChart.hideLoading();
myMapChart.setOption(chartOption);
});
};

var playFaCheList = function (data) {
//var html = ‘<!DOCTYPE html><html><head> <meta charset=”utf-8″> <meta name=”author” content=”” /> <style type=”text/css”> *{margin:10px;padding:10px;border:0px; }body{font-size:20px;color:”white”;} #demo{ overflow:hidden; height:100px; width:280px; margin:90px auto; position:relative; } #demo1{ height:auto; text-align:left; } #demo1 li{ list-style-type:none; height:22px; text-align:left; text-indent:15px; } </style></head><body><div id=”demo”> <ul id=”demo1″> ‘
//for (let i = 0 ; i < data.length; i++) {
// var item = data[i];
// html += ‘<li>’ + item.fromName + ‘=>’ + item.toName + ‘</li>’
//}
//html += ‘</li> </ul> <div id=”demo2″></div></div><script type=”text/JavaScript”> var speed=80 ; var demo=document.getElementById(“demo”); var demo1=document.getElementById(“demo1”); function Marquee(){ if(demo.scrollTop>=demo1.offsetHeight){ demo.scrollTop=0; } else{ demo.scrollTop=demo.scrollTop+1; } };var MyMar=setInterval(Marquee,speed); demo.onmouSEOver=function(){clearInterval(MyMar)}; demo.onmouSEOut=function(){MyMar=setInterval(Marquee,speed)};</script></body></html>’
//var x = document.getElementById(“iframe_play_fc”);
//x.srcdoc = html;
var ul = document.getElementById(“fachelist_ul”);
for (let i = 0 ; i < data.length; i++) {
let item = data[i];
let li = document.createElement(“li”);
li.innerHTML = ‘<li><a href=”#”>’
+ (i + 1) + ‘ . ‘
+ item.billdate + ‘ ‘
//+ item.inonevehicleflag
+ ‘ 车牌:’ + item.vehicleno + ‘ ‘
+ ‘<span style=”color:#ff8ba7″>’ + item.fromName + ‘=》’
+ item.full_toName + ‘</span> ‘
+ ‘</a></li>’;

ul.appendChild(li);
}
}
function map() {

myMapChart = echarts.getInstanceByDom(document.getElementById(‘map_1’));
if (myMapChart == undefined) {
myMapChart = echarts.init(document.getElementById(‘map_1’));
}

myMapChart.showLoading({
text: ‘正在加载数据…..’,
color: ‘#fff’,
textColor: ‘#fff’,
maskColor: ‘#46b3e6’,
zlevel: 0
});

$.ajax({
url: “/BI/GetgeoCoordData”,
type: “GET”,
data: { cache: false },
dataType: ‘json’,
cache: false,
success: function (result) {
if (result.status) {
if (result.data.length > 0) {
var data = JSON.parse(result.data);
geoCoordMap = $.extend(geoCoordMap, data);
lightProvince = data.provinceList;

$.ajax({
url: “/BI/GetgeoData”,
type: “GET”,
data: { cache: false },
dataType: ‘json’,
cache: false,
success: function (result) {
if (result.status) {
if (result.data.length > 0) {
var data = JSON.parse(result.data);
geoData = data;

seriesData();

$.ajax({
url: “/BI/GetProvinceCode”,
type: “GET”,
dataType: ‘json’,
success: function (result) {
if (result.status) {
if (result.data.length > 0) {
if (result.data == ‘china’) {
mapSelected = ‘china’;
}
else {
mapSelected = py_provinceMap[result.data];
}
loadMap(result.data);
}
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
return;
},
dataType: “json”
}).fail(function (jqXHR, textStatus) {
console.log(“Ajax Error: “, textStatus);
});

window.addEventListener(“resize”, function () {
myMapChart.resize();
});

var setProvinceCode = function (provinceCode) {
$.ajax({
url: “/BI/SetProvinceCode”,
type: “GET”,
data: { provinceCode: provinceCode },
dataType: ‘json’,
cache: false,
success: function (result) {
return;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
return;
},
dataType: “json”
}).fail(function (jqXHR, textStatus) {
console.log(“Ajax Error: “, textStatus);
});
};

myMapChart.on(‘click’, function (ev) {
if (isProvince(ev.name)) {
mapSelected = ev.name
console.log(mapSelected)
loadMap(provinceMap[ev.name]);
}
else if (isCity(ev.name)) {
mapSelected = ev.name;
loadMap(cityMap[ev.name]);
}
else {
mapSelected = ‘china’
loadMap(mapSelected)
}
});

//timeTicket && clearInterval(timeTicket);
//timeTicket = setInterval(function () {
// myMapChart.dispatchAction({
// type: ‘showTip’,
// seriesIndex: seriesIndex,
// dataIndex: dataIndex
// });
// count++;
// dataIndex++;
// if (dataIndex >= dataLength) {
// dataIndex = 0;
// }
//}, 5000);

//myMapChart.on(‘mouseover’, function (params) {
// console.log(params)
// clearInterval(timeTicket);
// myMapChart.dispatchAction({
// type: ‘showTip’,
// seriesIndex: seriesIndex,
// dataIndex: dataIndex
// });
// count++;
// dataIndex++;
// if (dataIndex >= dataLength) {
// dataIndex = 0;
// }
//}, 5000);

//myMapChart.on(‘mouseout’, function (params) {
// timeTicket && clearInterval(timeTicket);
// timeTicket = setInterval(function () {
// myMapChart.dispatchAction({
// type: ‘showTip’,
// seriesIndex: seriesIndex,
// dataIndex: dataIndex
// });
// count++;
// dataIndex++;
// if (dataIndex >= dataLength) {
// dataIndex = 0;
// }
// }, 5000)
//});
}
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
return;
},
dataType: “json”
}).fail(function (jqXHR, textStatus) {
console.log(“Ajax Error: “, textStatus);
});
}
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
return;
},
dataType: “json”
}).fail(function (jqXHR, status) {
console.log(“Ajax Error: “, status);
});
}

最后既可实现如上效果,点中相应的图例还进行预览。

图表

以这个图表为例,这个是柱状图。

同样先引入div图层

<div class="allnav" id="echart5_1"></div>

js调用实现

复制代码
  //员工学历分布
    function echarts_GetXLData() {
        var myChart = echarts.init(document.getElementById('echart5_1'));

        var itemStyle = {
            normal: {
                color: new echarts.graphic.LinearGradient(
                    0, 1, 0, 0, [{
                        offset: 0,
                        color: '#2af598'
                    }, {
                        offset: 1,
                        color: '#009efd'
                    }]
                ),
                barBorderRadius: 4
            },
            emphasis: {
                color: new echarts.graphic.LinearGradient(
                    0, 1, 0, 0, [{
                        offset: 0,
                        color: '#2af598'
                    }, {
                        offset: 1,
                        color: '#009efd'
                    }]
                ),
                barBorderRadius: 4
            }
        };

        // 指定图表的配置项和数据
        var option = {
            options: []
        };
        myChart.setOption(option);

        $.ajax({
            url: "/paymentWelfare/GetSalaryBigDataBYXL",
            type: "GET",
            data: { cache: false },
            dataType: 'json',
            cache: false,
            success: function (result) {
                if (result.status) {
                    if (result.data.length > 0) {
                        var data = JSON.parse(result.data);
                        var namearr = [];
                        var valuearr = [];
                        for (let i in data) {
                            namearr.push(data[i].name);
                            valuearr.push(data[i].value);
                        }
                        myChart.setOption({
                            tooltip: {
                                trigger: 'axis',
                                textStyle: {
                                    color: "#ffffff"
                                },
                                formatter: function (data) {
                                    var x = data[0];
                                    if (x == undefined) {
                                        return "";
                                    }
                                    return x.name + "<br/>" + x.seriesName + ":" + x.data;
                                }
                            },
                            legend: {
                                show: true,
                                x: 'right',
                                top: 15,
                                data: ['学历'],
                                textStyle: {
                                    color: 'white',
                                    fontSize: 15
                                }
                            },
                            //calculable: true,
                            grid: {
                                y: 40,
                                y2: 80,
                                left: '12%',
                                right: '4%',
                                bottom: '10%'
                            },
                            xAxis: [{
                                type: 'category',
                                axisLabel: {
                                    interval: 0,
                                    rotate: 0,
                                    textStyle: { fontSize: 12, color: '#ffffff' }
                                },
                                axisLine: {
                                    lineStyle: {
                                        color: 'white',
                                        width: 1
                                    }
                                },
                                data: namearr
                            }],
                            yAxis: [{
                                "axisTick": {
                                    "show": false
                                },
                                //"splitLine": {
                                //    "show": false
                                //},
                                type: 'value',
                                nameTextStyle: {
                                    color: "white",
                                    fontSize: 15,
                                    padding: 10
                                },
                                axisLabel: {
                                    textStyle: { color: 'white', fontSize: 12 },
                                    title: { textStyle: { color: 'white' } }
                                },
                                axisLine: {
                                    lineStyle: {
                                        color: 'white',
                                        width: 1
                                    }
                                },
                                splitLine: {
                                    lineStyle: {
                                        type: 'dashed',
                                        color: '#0d48e0'
                                    }
                                },
                            }],
                            series: [{
                                name: '学历',
                                yAxisIndex: 0,
                                type: 'bar',
                                itemStyle: itemStyle,
                                barWidth: 25,
                                label: {
                                    normal: {
                                        show: true,
                                        position: 'top',
                                        //formatter: '{c}' + '万元',
                                        color: 'white'
                                    }
                                },
                                data: valuearr
                            }]
                        });
                    }
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                return;
            },
            dataType: "json"
        }).fail(function (jqXHR, textStatus) {
            console.log("Ajax Error: ", textStatus);
        });

        window.addEventListener("resize", function () {
            myChart.resize();
        });
    }
复制代码

后端代码调用,获取数据

复制代码
        /// <summary>
        /// 学历
        /// </summary>
        /// <param name="cache"></param>
        /// <returns></returns>
        public ActionResult GetSalaryBigDataBYXL(bool cache = false)
        {
            string fileName = "xl.json";

            if (cache == true)
            {
                string jsonData = GetData(fileName);
                if (string.IsNullOrEmpty(jsonData) == false)
                {
                    return Json(new { status = true, data = jsonData }, JsonRequestBehavior.AllowGet);
                }
            }

            DataTable dt = GetHrBigData().Tables[3];
            List<nameValue> data = new List<nameValue> { };

            for (int i = 0; i < dt.Rows.Count; i++)
            {
                data.Add(new nameValue(dt.GetFieldValue<string>("name", i).ToStr(),
                    decimal.Round(dt.GetFieldValue<decimal>("count", i).ToDecimal(), 1)));
            }

            string json = data.ToJson();
            SaveData(fileName, json);

            return Json(new { status = true, data = json }, JsonRequestBehavior.AllowGet);
        }
复制代码

以上完成既可实现漂亮的图表了,其他饼图,折线图等等即同理实现,更新的实现效果多看看官网的帮助文档,你也可以实现漂亮的图表了,一起来交流学习吧!

消息滚动

 <div class="xpanel xpanel-r-t">
                        <div class="title"><span>员工提醒信息播报</span></div>
                        <div class="scrollDiv" id="fachelist_div" style="height:70%;">
                            <ul id="fachelist_ul"></ul>
                        </div>
                    </div>
复制代码
 (function ($) {
            $.fn.extend({
                Scroll: function (opt, callback) {
                    //参数初始化
                    if (!opt) var opt = {};
                    var _this = this.eq(0).find("ul:first");
                    var lineH = _this.find("li:first").height(),
                        //line = opt.line ? parseInt(opt.line, 10) : parseInt(this.height() / lineH, 10),
                        line = 1,
                        speed = opt.speed ? parseInt(opt.speed, 10) : 2000,
                        timer = opt.timer ? parseInt(opt.timer, 10) : 3000;
                    if (line == 0) line = 1;
                    var upHeight = 0 - line * lineH;
                    scrollUp = function () {
                        _this.animate({
                            marginTop: upHeight
                        }, speed, function () {
                            for (i = 1; i <= line; i++) {
                                _this.find("li:first").appendTo(_this);
                            }
                            _this.css({
                                marginTop: 0
                            });
                        });
                    }
                    _this.hover(function () {
                        clearInterval(timerID);
                    }, function () {
                        timerID = setInterval("scrollUp()", timer);
                    }).mouseout();
                }
            });
        })(jQuery);

        $(document).ready(function () {
            $("#fachelist_div").Scroll({
                line: 4,
                speed: 500,
                timer: 4000
            });
        });
复制代码

js文件获取数据代码

复制代码
    //消息提醒数据
    function echarts_GetMsgData() {
        var ul = document.getElementById("fachelist_ul");
        $.ajax({
            url: "/paymentWelfare/GetSalaryBigDataBYMsg",
            type: "GET",
            data: { cache: false },
            dataType: 'json',
            cache: false,
            success: function (result) {
                if (result.status) {
                    if (result.data.length > 0) {
                        var data = JSON.parse(result.data);
                        for (let i = 0 ; i < data.length; i++) {
                            let item = data[i];
                            let li = document.createElement("li");
                            li.innerHTML = '<li>' + '<span style="color:#FFFFFF">'
                                + (i + 1) + ' . '
                               + item.name + ' ' + '</span> '
                                + '</li>';
                            ul.appendChild(li);
                        }
                    }
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                return;
            },
            dataType: "json"
        }).fail(function (jqXHR, textStatus) {
            console.log("Ajax Error: ", textStatus);
        });
    }
复制代码

后台获取数据

复制代码
        /// <summary>
        /// 获取消息
        /// </summary>
        /// <param name="cache"></param>
        /// <returns></returns>
        public ActionResult GetSalaryBigDataBYMsg(bool cache = false)
        {
            string fileName = "msg.json";

            if (cache == true)
            {
                string jsonData = GetData(fileName);
                if (string.IsNullOrEmpty(jsonData) == false)
                {
                    return Json(new { status = true, data = jsonData }, JsonRequestBehavior.AllowGet);
                }
            }

            DataTable dt = GetHrBigData().Tables[5];
            List<nameValue> data = new List<nameValue> { };

            for (int i = 0; i < dt.Rows.Count; i++)
            {
                data.Add(new nameValue(dt.GetFieldValue<string>("name", i).ToStr(),
                    decimal.Round(dt.GetFieldValue<decimal>("count", i).ToDecimal(), 1)));
            }

            string json = data.ToJson();

            SaveData(fileName, json);

            return Json(new { status = true, data = json }, JsonRequestBehavior.AllowGet);
        }
复制代码

以上完成既可以实现消息的滚屏显示

以上从代码实现的角度介绍了我是怎么实现的这样一个效果的过程,工资大数据实现同人事一样,都是相同的控件去实现。

结束语

做好大数据分析任重道远,本人也是今年在这方面投入了大力气去学习,之前用WPF实现了一个,可以看我上一篇写的博文,就有介绍过,实现以上两个界面的大数据是用BS来实现的,图表全部用的是echarts,实现出来的图表费用漂亮,其实在做好这样的一个效果出来首先还得去网上找一个好的模板,再去做调整,完成从一个全新开发可能难度较大,样式布局啥的都不会这么专业,我其实是省去了前面部分的工作了,只花了三四天时间既完成了以上效果,速度还是挺快的,公司领导层对这效果也是非常满意的,以后会加强在这方面的研发,让公司的业务,财务数据都能很好在图表方面进行呈现出来,这样客户也会非常喜欢的。

以上个人愚见,一起加强学习进步!

附件:工资模块实现的最初模板。

https://files.cnblogs.com/files/luoyuhao/echartssjmoban8947202005210009.zip

企业级数据大屏设计如何实现,div+html+echarts - 气宇轩昂_2017 - 博客园

mikel阅读(849)

来源: 企业级数据大屏设计如何实现,div+html+echarts – 气宇轩昂_2017 – 博客园

大屏是什么?

大屏设计是最近比较流行的概念,一般按照功能来分有几种:

1. 可交互的触摸屏,大多运用在互动教学课程或者报告演示现场,用户可结合交互操作来阐述具体内容。设计师需要对交互形式和传达内容作统一思考设计。

 

2. 为某些特定的大型活动设计的专属大屏,比如说产品发布会、双11购物狂欢节。这类大屏需要结合音乐、场景、动效、灯光等多方面元素统一烘托,最终效果酷炫震撼,所有展现的内容都是定制化设计和开发的。

 

3.专为企业提供服务的可视化数据大屏,这一类大屏在商业中有其应用价值,它的特性是数据展示能力强,用一些较为常见的图表,如柱状图、饼图等来展现业务情况,使客户快速读懂数据背后的含义。

 

数据大屏为客户提供了政务、电商、客服、安全、金融等多个应用场景。

 

数据大屏和数据报告的区别是什么?

先来看一下业务场景中的报告和大屏是如何展现的:

报告

 

大屏

1.用户

数据报告的用户一般是数据分析师,分析师制作完报告,会把结果反馈给业务人员或者公司高层,为决策提供参考。而大屏的用户会更广,整个企业内的有关人员甚至所有员工都能够看大屏。

 

2.交互

一份完整的报告一般包括详细的分析过程,用户无法在不交互的情况下了解完整数据,比如说筛选、钻取、查看详情等等,都是很常用的分析功能。而大屏依靠视觉、动效,来传递有效信息,用户并不需要交互即可直观迅速了解内容。

 

3.时间

报告偏向展示一段时间内的数据,某些企业甚至能通过报告的日期筛选查询到从接入系统开始所有的历史数据。大屏更多起到的是监测职能,反馈的是实时信息,显示的是当下的数据。

 

4.展示

报告的阅读场景决定了它展示的侧重点在“精确”和“完整”,要达到这两点,不可避免有时候会牺牲一些视觉表现。例如:数据项过多的情况下,视觉感受比较拥挤。

 

对于大屏来说,即使只有当下的数据,用户也很难在短时间内get到关键,所以重要的是如何在有限的时间内传递出有效信息,即如何迅速提炼出重点数据并展示,即使这样会牺牲数据的完整性。

如果用一句话总结数据大屏的设计的关键:重点突出大于面面俱到。

 

大屏产生的过程

大部分人制作大屏的方式,只是一种图表的堆砌,先把需要的单个图表做完,然后简单地罗列组合在一起,最后改变一下整体颜色,就完成了。整个过程虽然不能说错,但并没有把大屏的优势发挥出来。一个完整的大屏设计过程应该包括以下步骤:

 

1.提炼信息

首先,我们要对数据进行分析,得出自己的结论。同样一份数据,因为不同的角度和思考方式,可能得出的观点很不一样。例如同样都是关于销售额的数据,有人希望知道各地销售额对比,有人希望了解销售额排名前五的商品类型,拿来数据就画图会让设计显得杂乱无章,读者也不知道要读什么数据。

所以在大屏设计之前,需要先和客户确认他们想要传达给目标用户的重点,这个重点是他们希望用户在读完这个大屏之后能够理解并记住的主要信息,很多公司都错误的认为,把多个数据塞进一个大屏中可以帮助提高公司的专业度,实际上这只能显示他们有很多数据。

为什么重点这么重要,试想一下,看大屏的人可能只会驻足在屏幕前一分钟,他们和大屏仅有的互动就是快速扫过整张图,在这一分钟内,到底能记住多少信息?设计师在每个设计环节都要牢牢记住这一点。

可以尝试着问客户两个问题:

(1)如果整个大屏只能展示一个最重要的信息,你希望是什么?

(2)你希望展现这些信息的理由是什么?通过客户的回答,你能了解他希望用户的关注点在哪里,从而提炼出设计重点。如果只有一个重点,放在最显眼的位置,如果有几个重点,尽量集中放置,吸引视觉焦点。

没有什么比加粗高亮数字更简单直接,保险大屏想传递的重心在于

(1)保费总额 。

(2)各地贡献的保费情况。

 

网络安全大屏,首先让用户通过直接的数字感知总体安全情况,其次详细查看当前攻击发生的源头和目的地。

 

整个客户服务的场景中,员工们是非常忙碌的,可能只有偶尔休息的间隙查看一下实时的服务情况,最希望传达给客服的信息集中设计在左半屏,

(1)呼叫量。

(2)满意度。其他信息的优先级相对较低。

 

 

2.选择图表

明确需要表达的信息和主题后,需要根据这个信息的数据关系,决定选择何种图表类型,以及要对图表作何种特别处理。

图表种类各式各样,有些图表很难界定是属于哪种关系,我见过讲图表关系和图表功能比较好的平台是Ant v的墨者学院,有兴趣的小伙伴可以了解一下。

Ant v把数据关系分成了9个大类,当你确定了某个数据关系类型后,就可以根据该数据的使用场景查找出相对应的图表和使用建议,并在其中进行选择。

以一份购物城数据为例:

 

3.制作图表

 

当确定了要使用哪些图表做图后,开始进入制作流程,影响最终图表展现效果的元素一般分为两个层面:

 

非数据层:

 

不受数据影响样式的元素,比如说背景、网格线、外边框等等。这类元素起到的是辅助阅读作用,但如果不加处理全部放出,视觉上会显得杂乱和不够简洁,干扰到你真正想展示的信息。对于这类元素,应该尽量隐藏和弱化。

 

隐藏

 

·      去除不必要的背景填充

 

·      去掉无意义的颜色变化

 

·      去掉不必要的外框

 

弱化

 

·      坐标轴淡色或隐藏

 

·      网格线淡色或隐藏

数据层:

受数据影响样式的元素,比如说柱状图的柱条长度,柱条颜色,柱条展示个数,气泡图气泡大小等等,这类元素的展示效果和图表本身的数据息息相关,一旦图表本身的数据比较极端,有可能会使得最终视觉展现不尽如人意,我们无法改变具体的数据,是否就完全无法操控这些元素了呢?

这里挑选了几个大屏中应用较多的图表,总结了其数据层样式的调整方式。

 

调整范围

 

·      截断超大值

当某一个值特别大时,绘制出的条形远远长于其他类别,导致其他条形被压缩,不便于比较。某条特别长,也可能会影响到整个大屏的排版平衡,可以采用截断选项的方式。

 

·      数据可以不从0开始

很多数据可视化工具里都有“数轴是否包括零”这一个选项,用户可通过这个功能来控制坐标轴的显示范围,例如下图,折线的波动范围比较小,走势起伏不明显,这时可以选择数据不从零开始,清晰地看出了折线的走势情况。当然,如果在平时的数据报告中,这样显示有夸大差异的嫌疑,不建议频繁使用。

 

避免重叠

 

·      避免负值被遮盖

当一些数值有负数时,标签和柱条离得较远,不便于阅读,如果标签紧贴柱条,又会发生重叠,比较好的方式是标签根据柱条的方向分别显示在坐标轴的两侧。

 

 

·      轴标签太长可横向放置

 

当轴标签太长时,虽然斜放可以避免重叠,但歪着头查看内容在浏览大屏的场景下对用户不是很友好,可以考虑把柱条横向放置,把标签置于柱条空隙之间。

 

精简数据项

 

·      饼图分类5~7项

在做数据报告时,不管有多少数据项,为了完整和精确性,所有的内容都会显示出来,但在大屏中,如此满的数据展示,不但忽略了视觉体验,还会让用户抓不住重点,对于饼图来说,建议扇区个数不要超过5个,例如保留占比前5的扇区,剩下的非重点数据全部归到“其他”。

·      保留前五和后五

如果柱状图的数据项过多,展示时会过于密集,建议先把数据项按照数值大小排序,然后将中间用户可能不是最关心的柱条折叠起来,只保留前五和后五的数据项。

强调重点

 

·      视觉高亮重要信息

先来看一张对比图,虽然左图颜色更加丰富,但是没有重点,视觉传达给用户的信息是没有主次的,而右图很明显想传达:这个数值有异常!请关注我!在大屏中,为了在短时间内让用户get到关键信息,应该尽量排除其他不重要的数据项干扰。

 

折线图中,只高亮重要数据点比每个节点都标注更能传递有效信息

 

在饼图中,因为颜色块大小代表占比多少,所以高亮的方式并不一定适用,我们可以通过分离某一块扇区达到强调重点的效果。

 

·      尽量减少图例

 

大屏的图表中应该尽量避免图例,图例会让用户不断在数据项和颜色块之间往返比对,耗费时间,还容易忘记重点,由于柱条个数经特殊处理后并不会很多,所以数据项名称可以直接标注。

 

总结

如果要对大屏设计的特点作一个总结:全屏时突出重点图表,单表时突出重点数据。把握好这点,至少可以避过大部分的坑。但一个高水准的大屏,还少不了一些细节的把控。比如:

 

1.  大屏风格是否符合业务主题,是热烈?是专业?是冷静?

 

2.  是否需要一些个性化的控件:例如时间器、轮播欢迎语等。

 

3.  是针对固定屏的定制化开发,还是考虑延展性的模块纵横栅格布局,对不同屏的适配是如何?

 

4.  现场投放大屏后,内容是否方便阅读,动效是否符合预期,色差是是否需要调整等等。

 

由于篇幅原因,这里不一一详细展开,如果有小伙伴对我们大屏感兴趣,可以加微信群:1092912327。

 

 

大屏是我们用来分享、沟通、传播信息的有效途径之一。它将会进化成一种新的媒体形式,在品牌推广、政务接待、商业沟通、数据监控等各个场景发挥重要作用。本文主要整理了一些大屏设计过程中的方法和原则,希望能够为大家提供一些借鉴思路。

写了几篇实现思路文章:

https://www.cnblogs.com/luoyuhao/p/13541260.html 怎么实现员工和工资大数据分析,echarts+js实现

https://www.cnblogs.com/luoyuhao/p/13867911.html SaaS系统怎么做物流行业年度经营报告,MVC+js+echarts实现

让Web API支持Protocol Buffers - 秋夜 - 博客园

mikel阅读(840)

来源: 让Web API支持Protocol Buffers – 秋夜 – 博客园

现在我们Web API项目基本上都是使用的Json作为通信的格式,随着移动互联网的兴起,Web API不仅其他系统可以使用,手机端也可以使用,但是手机端也有相对特殊的地方,网络通信除了wifi,还有蜂窝网络比如2G/3G,当手机处于这种网络环境下并且在一些偏僻或者有建筑物阻挡的地方,网络会变得非常差,之前我有测试过ProtoBuf和Json在序列化和反序列化上性能的对比,通过测试发现ProtoBuf在序列化和反序列化以及序列化后字节的长度和其他的框架相比都有很大的优势,所以这篇文章准备让Web API也支持Protocol Buffers。

要让Web API支持ProtoBuf就需在Web API的HttpConfigurationFormatters中注册MediaTypeFormatter,这里已经有现成的解决方案,我们直接使用。
1. 从nuget上下载WebApiContrib.Formatting.ProtoBuf.
2. 修改配置,新增config.Formatters.Add(new ProtoBufFormatter());

复制代码
        public static void Register(HttpConfiguration config)
        {
            // Web API 配置和服务

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",   //修改路由规则
                defaults: new { id = RouteParameter.Optional }
            );

            config.Formatters.Add(new ProtoBufFormatter());
        }
复制代码
完成上面两步, 我们的Web API就已经支持了ProtoBuf,我们可以新建一个Controller来进行测试。

复制代码
 public class UserController : ApiController
    {

        /// <summary>
        /// 注册用户
        /// </summary>
        /// <param name="userDto"></param>
        /// <returns></returns>
        public string Regist(UserDto userDto)
        {
            if (!string.IsNullOrEmpty(userDto.UserName) && !string.IsNullOrEmpty(userDto.Password))
            {
                return "regist success";
            }
            return "regis error";
        }

        //登陆
        public string Login(UserLoginDto userLoginDto)
        {
            if (userLoginDto.UserName == "sa")
            {
                return "login success";
            }
            return "loign fail";
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        public UserDto Get(string userName)
        {
            if (!string.IsNullOrEmpty(userName))
            {
                return new UserDto()
                {
                    UserName = "sa",
                    Password = "123",
                    Subscription = new List<string>() {"news", "picture"}
                };
            }
            return null;
        }
    }
复制代码
这里对实体也要进行修改。

复制代码
 [ProtoContract]
    public class UserDto
    {
        [ProtoMember(1)]
        public string UserName { get; set; }

        [ProtoMember(2)]
        public string Password { get; set; }

        [ProtoMember(3)]
        public List<string> Subscription { get; set; }
    }

  [ProtoContract]
    public class UserLoginDto
    {
        [ProtoMember(1)]
        public string UserName { get; set; }

        [ProtoMember(2)]
        public string Password { get; set; }
    }
复制代码
新建一个测试客户端,控制台就可以,引用WebApiContrib.Formatting.ProtoBuf和Web Api Client
将我们刚才创建的Dto复制到客户端,测试代码如下:

复制代码
  static void Main(string[] args)
        {
            var client = new HttpClient { BaseAddress = new Uri("http://192.168.16.9:8090/") };
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));

            Regist(client);
            Console.WriteLine("===================================");
            Login(client);
            Console.WriteLine("===================================");
            GetUser(client);
            Console.WriteLine("===================================");
            Console.ReadLine();
        }

        private static void Regist(HttpClient client)
        {
            //注册
            var userDto = new UserDto()
            {
                UserName = "sa",
                Password = "123456",
                Subscription = new List<string>() { "news", "picture" }
            };

            HttpResponseMessage response = client.PostAsync("api/User/Regist", userDto, new ProtoBufFormatter()).Result;

            if (response.IsSuccessStatusCode)
            {
                var p = response.Content.ReadAsAsync<string>(new[] { new ProtoBufFormatter() }).Result;
                Console.WriteLine(p);
            }
        }

        private static void Login(HttpClient client)
        {

            //登陆
            var userlogin = new UserLoginDto()
            {
                UserName = "sa",
                Password = "123456",
            };

            HttpResponseMessage response = client.PostAsync("api/User/login", userlogin, new ProtoBufFormatter()).Result;
            if (response.IsSuccessStatusCode)
            {
                var p = response.Content.ReadAsAsync<string>(new[] { new ProtoBufFormatter() }).Result;
                Console.WriteLine(p);
            }
        }

        private static void GetUser(HttpClient client)
        {

            HttpResponseMessage response = client.GetAsync("api/User/Get?userName=sa").Result;

            if (response.IsSuccessStatusCode)
            {
                var p = response.Content.ReadAsAsync<UserDto>(new[] { new ProtoBufFormatter() }).Result;

                Console.WriteLine(p.UserName + " " + p.Password);
                foreach (var s in p.Subscription)
                {
                    Console.WriteLine(s);
                }
            }
        }
复制代码
运行结果如下:
如上我们已经可以看到输出成功

K8s核心原理(一)之API Server - 简书

mikel阅读(1751)

来源: K8s核心原理(一)之API Server – 简书

image.png

参考:《kubernetes权威指南》

1. API Server简介

k8s API Server提供了k8s各类资源对象(pod,RC,Service等)的增删改查及watch等HTTP Rest接口,是整个系统的数据总线和数据中心。

kubernetes API Server的功能:

  1. 提供了集群管理的REST API接口(包括认证授权、数据校验以及集群状态变更);
  2. 提供其他模块之间的数据交互和通信的枢纽(其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd);
  3. 是资源配额控制的入口;
  4. 拥有完备的集群安全机制.

kube-apiserver工作原理图

2. 如何访问kubernetes API

k8s通过kube-apiserver这个进程提供服务,该进程运行在单个k8s-master节点上。默认有两个端口。

2.1. 本地端口

  1. 该端口用于接收HTTP请求;
  2. 该端口默认值为8080,可以通过API Server的启动参数“–insecure-port”的值来修改默认值;
  3. 默认的IP地址为“localhost”,可以通过启动参数“–insecure-bind-address”的值来修改该IP地址;
  4. 非认证或授权的HTTP请求通过该端口访问API Server。

2.2. 安全端口

  1. 该端口默认值为6443,可通过启动参数“–secure-port”的值来修改默认值;
  2. 默认IP地址为非本地(Non-Localhost)网络端口,通过启动参数“–bind-address”设置该值;
  3. 该端口用于接收HTTPS请求;
  4. 用于基于Tocken文件或客户端证书及HTTP Base的认证;
  5. 用于基于策略的授权;
  6. 默认不启动HTTPS安全访问控制。

2.3. 访问方式

Kubernetes REST API可参考https://kubernetes.io/docs/api-reference/v1.6/

2.3.1. curl

curl localhost:8080/api
curl localhost:8080/api/v1/pods
curl localhost:8080/api/v1/services
curl localhost:8080/api/v1/replicationcontrollers

2.3.2. Kubectl Proxy

Kubectl Proxy代理程序既能作为API Server的反向代理,也能作为普通客户端访问API Server的代理。通过master节点的8080端口来启动该代理程序。

kubectl proxy –port=8080 &

具体见kubectl proxy –help

[root@node5 ~]# kubectl proxy --help
To proxy all of the kubernetes api and nothing else, use:
kubectl proxy --api-prefix=/
To proxy only part of the kubernetes api and also some static files:
kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/
The above lets you 'curl localhost:8001/api/v1/pods'.
To proxy the entire kubernetes api at a different root, use:
kubectl proxy --api-prefix=/custom/
The above lets you 'curl localhost:8001/custom/api/v1/pods'
Usage:
 kubectl proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix] [flags]
Examples:
# Run a proxy to kubernetes apiserver on port 8011, serving static content from ./local/www/
$ kubectl proxy --port=8011 --www=./local/www/
# Run a proxy to kubernetes apiserver on an arbitrary local port.
# The chosen port for the server will be output to stdout.
$ kubectl proxy --port=0
# Run a proxy to kubernetes apiserver, changing the api prefix to k8s-api
# This makes e.g. the pods api available at localhost:8011/k8s-api/v1/pods/
$ kubectl proxy --api-prefix=/k8s-api
Flags:
 --accept-hosts="^localhost$,^127//.0//.0//.1$,^//[::1//]$": Regular expression for hosts that the proxy should accept.
 --accept-paths="^/.*": Regular expression for paths that the proxy should accept.
 --api-prefix="/": Prefix to serve the proxied API under.
 --disable-filter[=false]: If true, disable request filtering in the proxy. This is dangerous, and can leave you vulnerable to XSRF attacks, when used with an accessible port.
 -p, --port=8001: The port on which to run the proxy. Set to 0 to pick a random port.
 --reject-methods="POST,PUT,PATCH": Regular expression for HTTP methods that the proxy should reject.
 --reject-paths="^/api/.*/exec,^/api/.*/run": Regular expression for paths that the proxy should reject.
 -u, --unix-socket="": Unix socket on which to run the proxy.
 -w, --www="": Also serve static files from the given directory under the specified prefix.
 -P, --www-prefix="/static/": Prefix to serve static files under, if static file directory is specified.

Global Flags:
 --alsologtostderr[=false]: log to standard error as well as files
 --api-version="": The API version to use when talking to the server
 --certificate-authority="": Path to a cert. file for the certificate authority.
 --client-certificate="": Path to a client key file for TLS.
 --client-key="": Path to a client key file for TLS.
 --cluster="": The name of the kubeconfig cluster to use
 --context="": The name of the kubeconfig context to use
 --insecure-skip-tls-verify[=false]: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
 --kubeconfig="": Path to the kubeconfig file to use for CLI requests.
 --log-backtrace-at=:0: when logging hits line file:N, emit a stack trace
 --log-dir="": If non-empty, write log files in this directory
 --log-flush-frequency=5s: Maximum number of seconds between log flushes
 --logtostderr[=true]: log to standard error instead of files
 --match-server-version[=false]: Require server version to match client version
 --namespace="": If present, the namespace scope for this CLI request.
 --password="": Password for basic authentication to the API server.
 -s, --server="": The address and port of the Kubernetes API server
 --stderrthreshold=2: logs at or above this threshold go to stderr
 --token="": Bearer token for authentication to the API server.
 --user="": The name of the kubeconfig user to use
 --username="": Username for basic authentication to the API server.
 --v=0: log level for V logs
 --vmodule=: comma-separated list of pattern=N settings for file-filtered logging

2.3.3. kubectl客户端

命令行工具kubectl客户端,通过命令行参数转换为对API Server的REST API调用,并将调用结果输出。

命令格式:kubectl [command] [options]

具体可参考k8s常用命令

2.3.4. 编程方式调用

使用场景:

1、运行在Pod里的用户进程调用kubernetes API,通常用来实现分布式集群搭建的目标。

2、开发基于kubernetes的管理平台,比如调用kubernetes API来完成Pod、Service、RC等资源对象的图形化创建和管理界面。可以使用kubernetes提供的Client Library。

具体可参考https://github.com/kubernetes/client-go

3. 通过API Server访问Node、Pod和Service

k8s API Server最主要的REST接口是资源对象的增删改查,另外还有一类特殊的REST接口—k8s Proxy API接口,这类接口的作用是代理REST请求,即kubernetes API Server把收到的REST请求转发到某个Node上的kubelet守护进程的REST端口上,由该kubelet进程负责响应。

3.1. Node相关接口

关于Node相关的接口的REST路径为:/api/v1/proxy/nodes/{name},其中{name}为节点的名称或IP地址。

/api/v1/proxy/nodes/{name}/pods/    #列出指定节点内所有Pod的信息
/api/v1/proxy/nodes/{name}/stats/   #列出指定节点内物理资源的统计信息
/api/v1/prxoy/nodes/{name}/spec/    #列出指定节点的概要信息

这里获取的Pod信息来自Node而非etcd数据库,两者时间点可能存在偏差。如果在kubelet进程启动时加–enable-Debugging-handles=true参数,那么kubernetes Proxy API还会增加以下接口:

/api/v1/proxy/nodes/{name}/run      #在节点上运行某个容器
/api/v1/proxy/nodes/{name}/exec     #在节点上的某个容器中运行某条命令
/api/v1/proxy/nodes/{name}/attach   #在节点上attach某个容器
/api/v1/proxy/nodes/{name}/portForward   #实现节点上的Pod端口转发
/api/v1/proxy/nodes/{name}/logs     #列出节点的各类日志信息
/api/v1/proxy/nodes/{name}/metrics  #列出和该节点相关的Metrics信息
/api/v1/proxy/nodes/{name}/runningpods  #列出节点内运行中的Pod信息
/api/v1/proxy/nodes/{name}/debug/pprof  #列出节点内当前web服务的状态,包括CPU和内存的使用情况

3.2. Pod相关接口

/api/v1/proxy/namespaces/{namespace}/pods/{name}/{path:*}      #访问pod的某个服务接口
/api/v1/proxy/namespaces/{namespace}/pods/{name}               #访问Pod
#以下写法不同,功能一样
/api/v1/namespaces/{namespace}/pods/{name}/proxy/{path:*}      #访问pod的某个服务接口
/api/v1/namespaces/{namespace}/pods/{name}/proxy               #访问Pod

3.3. Service相关接口

/api/v1/proxy/namespaces/{namespace}/services/{name}

Pod的proxy接口的作用:在kubernetes集群之外访问某个pod容器的服务(HTTP服务),可以用Proxy API实现,这种场景多用于管理目的,比如逐一排查Service的Pod副本,检查哪些Pod的服务存在异常问题。

4. 集群功能模块之间的通信

kubernetes API Server作为集群的核心,负责集群各功能模块之间的通信,集群内各个功能模块通过API Server将信息存入etcd,当需要获取和操作这些数据时,通过API Server提供的REST接口(GET/LIST/WATCH方法)来实现,从而实现各模块之间的信息交互。

4.1. kubelet与API Server交互

每个Node节点上的kubelet定期就会调用API Server的REST接口报告自身状态,API Server接收这些信息后,将节点状态信息更新到etcd中。kubelet也通过API Server的Watch接口监听Pod信息,从而对Node机器上的POD进行管理。

监听信息 kubelet动作 备注
新的POD副本被调度绑定到本节点 执行POD对应的容器的创建和启动逻辑
POD对象被删除 删除本节点上相应的POD容器
修改POD信息 修改本节点的POD容器

4.2. kube-controller-manager与API Server交互

kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口,实时监控Node的信息,并做相应处理。

4.3. kube-scheduler与API Server交互

Scheduler通过API Server的Watch接口监听到新建Pod副本的信息后,它会检索所有符合该Pod要求的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上。

4.4. 特别说明

为了缓解各模块对API Server的访问压力,各功能模块都采用缓存机制来缓存数据,各功能模块定时从API Server获取指定的资源对象信息(LIST/WATCH方法),然后将信息保存到本地缓存,功能模块在某些情况下不直接访问API Server,而是通过访问缓存数据来间接访问API Server。

9人点赞

作者:沉沦2014
链接:https://www.jianshu.com/p/a25e9e613f2c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

localStorage使用总结 - 谢灿勇 - 博客园

mikel阅读(1072)

来源: localStorage使用总结 – 谢灿勇 – 博客园

一、什么是localStorage、sessionStorage

在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。

 

二、localStorage的优势与局限

localStorage的优势

1、localStorage拓展了cookie的4K限制

2、localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的

localStorage的局限

1、浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性

2、目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换

3、localStorage在浏览器的隐私模式下面是不可读取的

4、localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡

5、localStorage不能被爬虫抓取到

localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空

这里我们以localStorage来分析

 

三、localStorage的使用

localStorage的浏览器支持情况:

这里要特别声明一下,如果是使用IE浏览器的话,那么就要UserData来作为存储,这里主要讲解的是localStorage的内容,所以userData不做过多的解释,而且以博主个人的看法,也是没有必要去学习UserData的使用来的,因为目前的IE6/IE7属于淘汰的位置上,而且在如今的很多页面开发都会涉及到HTML5\CSS3等新兴的技术,所以在使用上面一般我们不会去对其进行兼容

首先在使用localStorage的时候,我们需要判断浏览器是否支持localStorage这个属性

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
            return false;
        }else{
            //主逻辑业务
        }
复制代码

 

localStorage的写入,localStorage的写入有三种方法,这里就一一介绍一下

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
            return false;
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.a=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(typeof storage["a"]);
            console.log(typeof storage["b"]);
            console.log(typeof storage["c"]);
        }
复制代码

 

运行后的结果如下:

这里要特别说明一下localStorage的使用也是遵循同源策略的,所以不同的网站直接是不能共用相同的localStorage

最后在控制台上面打印出来的结果是:

不知道各位读者有没有注意到,刚刚存储进去的是int类型,但是打印出来却是string类型,这个与localStorage本身的特点有关,localStorage只支持string类型的存储。

localStorage的读取

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.a=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(typeof storage["a"]);
            console.log(typeof storage["b"]);
            console.log(typeof storage["c"]);
            //第一种方法读取
            var a=storage.a;
            console.log(a);
            //第二种方法读取
            var b=storage["b"];
            console.log(b);
            //第三种方法读取
            var c=storage.getItem("c");
            console.log(c);
        }
复制代码

 

这里面是三种对localStorage的读取,其中官方推荐的是getItem\setItem这两种方法对其进行存取,不要问我这个为什么,因为这个我也不知道

我之前说过localStorage就是相当于一个前端的数据库的东西,数据库主要是增删查改这四个步骤,这里的读取和写入就相当于增、查的这两个步骤

下面我们就来说一说localStorage的删、改这两个步骤

改这个步骤比较好理解,思路跟重新更改全局变量的值一样,这里我们就以一个为例来简单的说明一下

 

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.b=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(storage.a);
            // console.log(typeof storage["a"]);
            // console.log(typeof storage["b"]);
            // console.log(typeof storage["c"]);
            /*分割线*/
            storage.a=4;
            console.log(storage.a);
        }
复制代码

 

这个在控制台上面我们就可以看到已经a键已经被更改为4了

localStorage的删除

1、将localStorage的所有内容清除

复制代码
var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            console.log(storage);
            storage.clear();
            console.log(storage);
复制代码

 

2、 将localStorage中的某个键值对删除

 

复制代码
var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            console.log(storage);
            storage.removeItem("a");
            console.log(storage.a);
复制代码

 

控制台查看结果

localStorage的键获取

复制代码
var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            for(var i=0;i<storage.length;i++){
                var key=storage.key(i);
                console.log(key);
            }
复制代码

 

使用key()方法,向其中出入索引即可获取对应的键

 

四、localStorage其他注意事项

一般我们会将JSON存入localStorage中,但是在localStorage会自动将localStorage转换成为字符串形式

这个时候我们可以使用JSON.stringify()这个方法,来将JSON转换成为JSON字符串

示例:

复制代码
if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            var data={
                name:'xiecanyong',
                sex:'man',
                hobby:'program'
            };
            var d=JSON.stringify(data);
            storage.setItem("data",d);
            console.log(storage.data);
        }
复制代码

 

读取之后要将JSON字符串转换成为JSON对象,使用JSON.parse()方法

复制代码
var storage=window.localStorage;
            var data={
                name:'xiecanyong',
                sex:'man',
                hobby:'program'
            };
            var d=JSON.stringify(data);
            storage.setItem("data",d);
            //将JSON字符串转换成为JSON对象输出
            var json=storage.getItem("data");
            var jsonObj=JSON.parse(json);
            console.log(typeof jsonObj);
复制代码

打印出来是Object对象

另外还有一点要注意的是,其他类型读取出来也要进行转换