八、Echart图表 之 series盒须图(箱体图)基本使用与配置大全_echarts 盒须图-CSDN博客

mikel阅读(671)

来源: 八、Echart图表 之 series盒须图(箱体图)基本使用与配置大全_echarts 盒须图-CSDN博客

🍓 作者主页:💖仙女不下凡💖

🍓 前言介绍:以下👇内容是我个人对于该技术的总结,如有不足与错误敬请指正!

🍓 欢迎点赞👍 收藏⭐ 留言📝 相关账号会持续发布关于文章Echart的相关文章欢迎持续关注!

🍓Echart官网地址:https://echarts.apache.org/examples/zh/index.html#chart-type-line

✨箱体图含义:1.箱子的中间一条线,是数据的中位数,代表了样本数据的平均水平。
2.箱子的上下限,分别是数据的上四位数和下四分位数,这意味着箱子包含了50%的数据,因此箱子的宽度在一定程度上反应了数据的波动程度。
3.在箱子的上方和下方,又各有一条线,有时候代表着最大最小值,有时候会有一些点“冒出去”。请千万不要纠结,理解成“异常值”就好。

✨盒须图(箱体图)的基本使用:我看了一下网上了一些案例全部都是用dataSet映射处理的数据,看的不太明白,下面我就使用series.data处理数据绘制简单的箱体图,如下图

itemStyle:盒须图样式
itemStyle.decal:图形的贴花图案
emphasis:盒须图高亮样式
blur:淡出时的图形样式和标签样式
select:数据选中时的图形样式和标签样式
data:数据
markPoint:图表标注
markLine:图表标线
markArea:图表标域,常用于标记图表中某个范围的数据,例如标出某段时间投放了广告
universalTransition:全局过渡动画相关的配置
tooltip:本系列特定的 tooltip 设定
💕 盒须图(箱体图)还有很多配置属性,具体使用请参考一下内容

series: [{
type: ‘boxplot’, //这个配置表示显示的是箱体图
id: ”,
coordinateSystem: ‘cartesian2d’, //该系列使用的坐标系。使用二维的直角坐标系,通过xAxisIndex, yAxisIndex指定相应的坐标轴组件
xAxisIndex: 0, //使用的 x 轴的index,在单个图表实例中存在多个 x 轴的时候有用
xAxisIndex: 0, //使用的 x 轴的index,在单个图表实例中存在多个 x 轴的时候有用
name: ”, //系列名称,用于tooltip的显示,legend的图例筛选
colorBy: ‘series’, //从调色盘option.color中取色策略。series同一系列中的所有数据都是用相同的颜色/data每个数据项都使用不同的颜色
legendHoverLink: true, //是否启用图例hover时的联动高亮
hoverAnimation: true, //是否开启hover在box上的动画效果
layout: ‘horizontal’, //布局方式,属性值:horizontal/vertical
boxWidth: [7, 50], //box宽度。属性值:number/数组(意思是宽度的上下限:[min, max])
itemStyle: {
color: none, /颜色,参考下面的文章/
borderColor: #666, //写法参考color
borderWidth: 1,
borderType: ‘solid’,
borderDashOffset: 0, //设置虚线偏移量,搭配borderType实现灵活的虚线效果
borderCap: ‘butt’, //指定线段末端绘制方式,属性值:butt/round/square
borderJoin: ‘bevel’, //设置2个长度不为0的相连部分(线段,圆弧,曲线)如何连接在一起的属性(长度为0的变形部分,其指定的末端和控制点在同一位置,会被忽略),属性值:bevel/round/miter
borderMiterLimit: 10, //设置斜接面限制比例,只有当borderJoin为miter时才有效
shadowBlur: ”, //图形阴影模糊大小。该属性配合shadowColor/shadowOffsetX/shadowOffsetY一起设置图形的阴影效果
shadowColor: ”, //阴影颜色。支持的格式同color
shadowOffsetX: 0, //阴影水平方向上的偏移距离
shadowOffsetY: 0, //阴影垂直方向上的偏移距离
opacity: 1, //图形透明度
decal: {
symbol: ‘rect’, //贴花的图案,属性值也可以是数组表示循环使用,circle/rect/roundRect/triangle/diamond/pin/arrow/none
symbolSize: 1, //表示占图案区域的百分比,取值范围:0-1
symbolKeepAspect: true, //是否保持图案的长宽比
color: ”, /颜色,参考下面的文章/
backgroundColor: ”,
dashArrayX: 5,
dashArrayY: 5,
rotation: 0, //图案的整体旋转角度(弧度制)
maxTileWidth: 512, //生成的图案在未重复之前的宽度上限。通常不需要设置该值,当你发现图案在重复的时候出现不连续的接缝时,可以尝试提高该值
maxTileHeight: 512
}
},
emphasis: {
disabled: false, //是否关闭高亮状态。默认false
focus: ‘none’, //高亮图形时,是否淡出其它数据图形已达到聚焦的效果。属性值:none/self/series
blurScope: ‘coordinateSystem’, //在开启focus时,可以通过blurScope配置淡出的范围
itemStyle: {} //图形样式,参考上面itemStyle配置,但是没有itemStyle.decal
},
blur: {
itemStyle: {} //图形样式,参考上面itemStyle配置,但是没有itemStyle.decal
},
select: {
disabled: false, //是否可以被选中。默认false,在开启selectedMode时有效,可用于关闭部分数据
itemStyle: {} //图形样式,参考上面itemStyle配置,但是没有itemStyle.decal
},
selectedMode: []/{}, //选中模式的配置
dimensions: ”, //使用dimensions定义series.data或者dataset.source的每个维度的信息。
encode: ”, //可以定义 data 的哪个维度被编码成什么
dataGroupId: ”, //可以定义 data 的哪个维度被编码成什么
data: [],
markPoint: {},
markLine: {},
markArea: {},
clip: type,
z: 2,
silent: false,
animationDuration: 800,
animationEasing: elasticOut,
animationDelay: 0,
universalTransition: {},
tooltip: {}
}]
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
👉推荐相关文章:Echart图表 之 基本使用及配置项

👉推荐相关文章:Echart图表 之 title配置项大全

👉推荐相关文章:Echart图表 之 颜色color配置项大全

👉推荐相关文章:Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全

👉推荐相关文章:Echart图表 之 legend图例组件配置项大全

👉推荐相关文章:Echart图表 之 tooltip提示框组件配置项大全

👉推荐相关文章:Echart图表 之 toolbox工具栏组件配置项大全
————————————————
版权声明:本文为CSDN博主「仙女不下凡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_55181759/article/details/124819828

二、Echart图表 之 title配置项大全_echart标题-CSDN博客

mikel阅读(258)

来源: 二、Echart图表 之 title配置项大全_echart标题-CSDN博客

title就是图表标题,可以控制它的属性,如颜色、位置等,当有副标题的时候title有两种写法,可以写为数组或对象,下面我就以对象形式整理title的配置项,以数组形式写例子。
title: {
show: true, // 是否显示标题组件,(true/false)
text: ”, // 主标题文本,支持使用\n换行
textAlign:’auto’, //整体水平对齐(包括text和subtext)
textVerticalAlign:’auto’,//整体的垂直对齐(包括text和subtext)
padding:0, // 标题内边距 写法如[5,10]||[ 5,6, 7, 8] ,
left:’auto’, // title组件离容器左侧距离,写法如’5’||’5%’
right:’auto’, //’title组件离容器右侧距离
top:’auto’, // title组件离容器上侧距离
bottom:’auto’, // title组件离容器下侧距离
borderColor: ”, // 标题边框颜色
borderWidth: 1, // 边框宽度(默认单位px)
textStyle: { // 标题样式
color: ”, //字体颜色
fontStyle: ”, //字体风格
fontSize: 14, //字体大小
fontWeight: 400, //字体粗细
fontFamily: ”, //文字字体
lineHeight: ” //字体行高
align:’center’,//文字水平对齐方式(left/right)
verticalAlign:’middle’,//文字垂直对齐方式(top/bottom)
},
subtext: ”, // 副标题
subtextStyle: { // 副标题样式
color: ‘#ccc’,
fontStyle:’normal’,
fontWeight:’normal’,
fontFamily:’sans-serif’,
fontSize:18,
lineHeight:18,
}
}

下面为大家举个例子,展示title在图表中使用的实际效果与数组写法,效果与代码如下

title: [
{ // 标题
text: ‘Michelson-Morley Experiment’,
left: ‘center’
},
{ // 副标题
text: ‘upper: Q3 + 1.5 * IQR \nlower: Q1 – 1.5 * IQR’, // ‘/n’代表换行
borderColor: ‘#999’,
borderWidth: 1, // 边框宽度(默认单位px)
textStyle: { // 标题样式
fontSize: 14
},
left: ‘10%’, // 位置
top: ‘90%’ // 位置
}
],

👉推荐相关文章:Echart图表 之 基本使用及配置项

👉推荐相关文章:Echart图表 之 颜色color配置项大全

👉推荐相关文章:Echart图表 之 legend图例组件配置项大全

👉推荐相关文章:Echart图表 之 X轴(xAxis)与Y轴(yAxis)配置项大全

👉推荐相关文章:Echart图表 之 tooltip提示框组件配置项大全

👉推荐相关文章:Echart图表 之 toolbox工具栏组件配置项大全
————————————————
版权声明:本文为CSDN博主「仙女不下凡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_55181759/article/details/124838942

一、Echart图表 之 基本使用及配置项_仙女不下凡的博客-CSDN博客

mikel阅读(282)

来源: 一、Echart图表 之 基本使用及配置项_仙女不下凡的博客-CSDN博客

一、基本使用
请在阅读文章时参考Echart官网地址中的内容配合了解,首先我们知道Echart图表中有许多类型,如折线图、柱状图、饼形图等,下面我以折线图为例讲解Echart图表的基本使用。
<!–示例代码–>
<template>
<div class=”main”></div>
</template>
<script>
import * as echarts from ‘echarts’;

var chartDom = document.getElementById(‘main’);
var myChart = echarts.init(chartDom);
var option;

option = {
xAxis: {
type: ‘category’,
data: [‘Mon’, ‘Tue’, ‘Wed’, ‘Thu’, ‘Fri’, ‘Sat’, ‘Sun’]
},
yAxis: {
type: ‘value’
},
series: [
{
data: [150, 230, 224, 218, 135, 147, 260],
type: ‘line’
}
]
};

option && myChart.setOption(option);
</script>
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
效果如下:

大家可能会有疑问为什么x轴与y轴的type值不同?那么我就交换这个值演示改代码的效果。

// 代码如下,其实就是将type颠倒了
xAxis: {
type: ‘value’,
data: [‘Mon’, ‘Tue’, ‘Wed’, ‘Thu’, ‘Fri’, ‘Sat’, ‘Sun’]
},
yAxis: {
type: ‘category’
},
1
2
3
4
5
6
7
8
所以我们得出结论xAxis与yAxis中的type属性值是呼应的,所以请大家在理解Echart配置时一定要举一反三,它的配置非常非常多理解比硬记要更加靠谱。
配置项的写法与位置
位置不同:在了解Echart的配置项时,有的属性可以在option中全局配置,也可以局部配置。
写法不同:另外配置项有时候是对象,有的时候是数组,也可以是函数。
出现的位置不同代表的含义不同,但是你会发现即使形式或者位置不同但是它使用起来是一样的,下面我就以color为例演示一下
1.全局配置color且为数组类型。

option = {
/这就是颜色的全局配置,但是如果不配置颜色echart有默认颜色这里不详解就是理解概念/
color: [‘red’, ‘green’] // 配置颜色会把默认颜色覆盖
}
1
2
3
4
2.局部配置color

option = {
title: {
textStyle: {
color: ‘red’ // 这里代表字体颜色是红色
}
}
}
1
2
3
4
5
6
7
3.对象格式color

color: { /这里是线性渐变颜色,这里理解即可不做详解/
type: ‘linear’,
x: 1,
y: 0,
x2: 0,
y2: 0,
colorStops: [{
offset: 0,
color: ‘rgba(240,47,194,0.7)’// 0%处的颜色
}, {
offset: 1,
color: ‘rgba(149,111,212,0.7)’ // 100%处的颜色
}]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
二、配置项option包含属性
option = {
title: {}, //标题,如有副标题可以写成数组
color: [], //颜色
backgroundColor: ”, //背景色
darkMode: ”, //是否是暗黑模式
dataZoom: [], //
dataset: [{}], //数据集合
toolbox: {}, //
tooltip: {}, //提示框
grid: {}, //图表上下左右距离盒子的距离
xAxis: {}, //x轴
yAxis: {}, //y轴
series: [{
type: ” //graph关系图,line折线图,bar柱形图,boxplot箱体图等
}],
graphic: [],
baSEOption: {},
legend: {}, //控制图例组件
calculable: ”,
options: [],
brush: {},
animation: true, //是否开启动画,布尔值
animationThreshold: 2000, //
animationDuration: 1000, //
animationEasing: ‘cubicOut’, //
animationDelay: 0, //初始动画的延迟,支持回调函数
animationDurationUpdate: 300, //数据更新动画的时长,支持回调函数
animationEasingUpdate: ‘cubicInOut’, //数据更新动画的缓动效果
animationDelayUpdate: 0, //数据更新动画的延迟,支持回调函数
blendMode: ”, //图形的混合模式
hoverLayerThreshold: 3000, /**图形数量阈值**/
useUTC: false, //是否使用UTC时间
options: ”,
]};
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
上述配置项具体作用请参考下面🍀三、各个配置项属性大全其余比较复杂的属性我会用单独的文章讲解
三、各个配置项属性大全
配置项 作用 说明
title 标题组件,包含主标题和副标题 🍀详情参考文章:Echart图表 之 title配置项大全
color 调色盘颜色列表 可以全局配置,也可以局部配置,🍀详情参考文章:Echart图表 之 颜色color配置项大全
legend 图例组件 效果如图🍀详情参考文章:Echart图表 之 legend图例组件配置项大全
grid 直角坐标系内绘图网格 可以理解为控制图表整体Echart布局,🍀详情参考文章:Echart图表 之 grid组件用法 直角坐标系内绘图网格基本使用与配置大全
xAxis/xAxis X/Y轴组件 **🍀详情参考文章:[Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全]
(https://blog.csdn.net/weixin_55181759/article/details/124835864)**
dataZoom 区域缩放 正在更新中,请期待22.11.01…
backgroundColor 背景色 默认无背景支持使用rgb(255,255,255),rgba(255,255,255,1),#fff等方式设置
darkMode 是否是暗黑模式 默认会根据backgroundColor亮度自动设置。如果设置了容器背景色而无法判断到,就可以使用该配置手动指定,echarts会根据是否是暗黑模式调整文本等的颜色。该配置常用于主题中
animation 是否开启动画 布尔值
animationThreshold 是否开启动画的阈值 当单个系列显示的图形数量大于这个阈值时会关闭动画
animationDuration 初始动画时长 可以通过每个数据返回不同的时长实现更戏剧初始动画效果,支持回调函数animationDuration: function (idx) {return idx * 100}
animationEasing 初始动画缓动效果 属性值cubicOut
animationDelay 初始动画延迟 可以通过每个数据返回不同的delay时间实现更戏剧的初始动画效果,毫秒,支持回调函数
animationDurationUpdate 数据更新动画的时长 毫秒,支持回调函数
animationEasingUpdate 数据更新动画的缓动效果 属性值cubicInOut
animationDelayUpdate 数据更新动画的延迟 毫秒,支持回调函数
blendMode 图形的混合模式 默认为source-over。支持每个系列单独设置。lighter也是比较常见的一种混合模式,该模式下图形数量集中的区域会颜色叠加成高亮度的颜色(白色)。常常能起到突出该区域的效果
hoverLayerThreshold 图形数量阈值 这个我没看懂,后续更新~~~~
useUTC 是否使用UTC时间 true表示axis.type为time时,依据UTC时间确定tick位置,并且 axisLabel和tooltip默认展示的是UTC时间,false则依据的是本地时间
options 用于timeline的option数组,数组每一项是一个echarts option
tooltip 提示框组件 🍀详情参考文章:Echart图表 之 tooltip提示框组件配置项大全
toolbox 工具栏组件 效果如图:🍀详情参考文章:Echart图表 之 toolbox工具栏组件配置项大全
series 数据配置项 🍀详情参考文章:Echart图表 之 series盒须图(箱体图)基本使用与配置大全 这个里面主要是控制图形的数据与图表的类型,如柱形图、饼图、箱体图等,配置项如下图,
暂未写完,持续更新中…
————————————————
版权声明:本文为CSDN博主「仙女不下凡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_55181759/article/details/124805178

三、Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全_echart yaxis-CSDN博客

mikel阅读(440)

来源: 三、Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全_echart yaxis-CSDN博客

参考文章:Echarts X轴(xAxis)

xAxis与yAxis中有很多配置项,下面我以xAxis进行详解,yAxis参考xAxis即可

nameTextStyle:坐标轴名称的文字样式

axisLine:坐标轴轴线相关设置。

axisTick:坐标轴刻度相关设置。

axisLabel:坐标轴刻度标签的相关设置。

splitLine: 坐标轴在 grid 区域中的分隔线设置。

splitArea :坐标轴在 grid 区域中的分隔区域,默认不显示。

上述xAxis与yAxis中的配置项,其中也会含有很多属性,具体使用请参考一下内容。

xAxis: {
id: ”,
show: true, //是否显示x轴
gridIndex: 0, //轴所在grid索引,默认位于第一个grid
alignTicks: false, //在多个轴为数值轴的时候,可以开启该配置项自动对齐刻度。只对’value’和’log’类型的轴有效
position: ‘top’, //轴的位置(top/bottom)
offset: 0, //轴相对于默认位置的偏移,在相同的position上有多个轴时有用
type: ‘category’, //坐标轴类型,值category/value,与y轴呼应,若x轴配置category则y轴配置value
name: ”, //坐标轴名称
nameLocation: ‘end’, //坐标轴名称显示位置,可选值start/middle[或者center]/end
nameTextStyle: {}, //一般样式也很少直接修改且内容过多待更新… …
nameGap: 15, //坐标轴名称与轴线间距离
nameRotate: 10, //坐标轴名字旋转,角度值
inverse: false, //是否是反向坐标轴
boundaryGap: [‘20%’, ‘20%’], // 坐标轴两边留白策略,也可以使用布尔值,true居中
min: ”, //刻度最小值
max: ”, //刻度最大值
scale: false, //只在数值轴中type: ‘value’有效, 设置min和max后无效。是否是脱离 0 值比例。设置成true后坐标刻度不会强制包含零刻度。在双数值轴的散点图中较有用
splitNumber: 5, //坐标轴的分割段数(预估值)
minInterval: 0, //自动计算坐标轴最小间隔,例:设置成1,刻度没有小数
maxInterval: ”, //自动计算坐标轴最大间隔
axisLine: {
show: true, // 是否显示坐标轴轴线
symbol: [‘none’, ‘arrow’], // 轴线两端箭头,两个值,none表示没有箭头,arrow表示有箭头
symbolSize: [10, 15], // 轴线两端箭头大小,数值一表示宽度,数值二表示高度
lineStyle: {
color: ‘#333’, // 坐标轴线线的颜色
width: ‘5’, // 坐标轴线线宽
type: ‘solid’, // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
axisTick: {
show: true, // 是否显示坐标轴刻度
inside: true, // 坐标轴刻度是否朝内,默认朝外
length: 5, //坐标轴刻度的长度
lineStyle: {
color: ‘#FFF’, //刻度线的颜色
width: 10, //坐标轴刻度线宽
type: ‘solid’, //坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
axisLabel: {
show: true, //是否显示刻度标签
interval: ‘0’, //坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
inside: true, //刻度标签是否朝内,默认朝外
rotate: 90, //刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠;旋转的角度从-90度到90度
margin: 10, //刻度标签与轴线之间的距离
// formatter 刻度标签的内容格式器,支持字符串模板和回调函数两种形式
color: ‘#FFF’, // 刻度标签文字的颜色
fontStyle: ‘normal’, // 字体的风格(normal无样式;italic斜体;oblique倾斜字体)
fontWeight: ‘normal’, // 字体的粗细(normal无样式;bold加粗;bolder加粗再加粗;lighter变细;数字定义粗细也可以取值范围100至700)
fontSize: ’20’, //文字字体大小
align: ‘left’, // 文字水平对齐方式,默认自动(left/center/right)
verticalAlign: ‘left’, // 文字垂直对齐方式,默认自动(top/middle/bottom)
lineHeight: ’50’, // 行高
backgroundColor: ‘red’, // 文字块背景色,例:#123234, red, rgba(0,23,11,0.3)
},
splitLine: {
show: true, // 是否显示分隔线。默认数值轴显示,类目轴不显示
interval: ‘0’, // 坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
color: [‘#ccc’], //color分隔线颜色,可设置单个颜色,也可设置颜色数组,分隔线会按数组中颜色顺序依次循环设置颜色
width: 3, // 分隔线线宽
type: ‘solid’, // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
splitArea: {
show: true, // 是否显示分隔区域
interval: ‘0’, // 坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
areaStyle: {
color: [‘rgba(250,250,250,0.3)’,’rgba(200,200,200,0.3)’], //color分隔区域颜色。分隔区会按数组中颜色顺序依次循环设置颜色。默认是一个深浅的间隔色
opacity: 1, // 图形透明度。支持从0到1的数字,为0时不绘制该图形
},
},
data: {
textStyle: {
color: ‘#FFF’, // 文字的颜色
fontStyle: ‘normal’, // 文字字体的风格(normal无样式;italic斜体;oblique倾斜字体)
fontWeight: ‘normal’, //字体的粗细(normal无样式;bold加粗;bolder加粗再加粗;lighter变细;数字定义粗细也可以取值范围100至700)
fontSize: ’20’, // 文字字体大小
align: ‘left’, // 文字水平对齐方式,默认自动(left/center/right)
verticalAlign: ‘left’, // 文字垂直对齐方式,默认自动(top/middle/bottom)
lineHeight: ’50’, // 行高
backgroundColor: ‘red’, // 文字块背景色,例:#123234, red, rgba(0,23,11,0.3)
}
}
}

👉推荐相关文章:Echart图表 之 基本使用及配置项

👉推荐相关文章:Echart图表 之 title配置项大全

👉推荐相关文章:Echart图表 之 颜色color配置项大全

👉推荐相关文章:Echart图表 之 legend图例组件配置项大全

👉推荐相关文章:Echart图表 之 tooltip提示框组件配置项大全

👉推荐相关文章:Echart图表 之 toolbox工具栏组件配置项大全
————————————————
版权声明:本文为CSDN博主「仙女不下凡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_55181759/article/details/124835864

【精选】教你从零开始画echarts地图_cRack_cLick的博客-CSDN博客

mikel阅读(311)

来源: 【精选】教你从零开始画echarts地图_cRack_cLick的博客-CSDN博客

echarts地图制作
离线地图下载地址https://datav.aliyun.com/tools/atlas/index.html

echarts文档地址https://echarts.apache.org/zh/option.html

基于VUE编写,其他框架请自行转换,大同小异

基础配置
先让地图内容出来,npm安装步骤省略,请参考官方文档,创建的div必须设置宽度和高度,关于图表的宽高自适应,参考我的另一篇文章

<template>
<div class=”content”>
<div ref=”charts” style=”width: 1000px; height: 800px”></div>
</div>
</template>

<script>
import * as echarts from “echarts”;
import zhongguo from “@/assets/mapJson/data-city.json”
export default {
created () {
this.$nextTick(() => {
this.initCharts();
})
},
methods: {
initCharts() {
const charts = echarts.init(this.$refs[“charts”]);
const option = {
// 背景颜色
backgroundColor: “#404a59”,
// 提示浮窗样式
tooltip: {
show: true,
trigger: “item”,
alwaysShowContent: false,
backgroundColor: “#0C121C”,
borderColor: “rgba(0, 0, 0, 0.16);”,
hideDelay: 100,
triggerOn: “mousemove”,
enterable: true,
textStyle: {
color: “#DADADA”,
fontSize: “12”,
width: 20,
height: 30,
overflow: “break”,
},
showDelay: 100
},
// 地图配置
geo: {
map: “china”,
label: {
// 通常状态下的样式
normal: {
show: true,
textStyle: {
color: “#fff”,
},
},
// 鼠标放上去的样式
emphasis: {
textStyle: {
color: “#fff”,
},
},
},
// 地图区域的样式设置
itemStyle: {
normal: {
borderColor: “rgba(147, 235, 248, 1)”,
borderWidth: 1,
areaColor: {
type: “radial”,
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{
offset: 0,
color: “rgba(147, 235, 248, 0)”, // 0% 处的颜色
},
{
offset: 1,
color: “rgba(147, 235, 248, .2)”, // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
shadowColor: “rgba(128, 217, 248, 1)”,
shadowOffsetX: -2,
shadowOffsetY: 2,
shadowBlur: 10,
},
// 鼠标放上去高亮的样式
emphasis: {
areaColor: “#389BB7”,
borderWidth: 0,
},
},
},
};
// 地图注册,第一个参数的名字必须和option.geo.map一致
echarts.registerMap(“china”,zhongguo)

charts.setOption(option);
},
},
};
</script>

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
98
99
100
101
102
103
这是最基础的配置,外加了一些我自己写的样式,使地图美观一些,如果你完全的复制,并且china.json文件也引入了,那么你会看到如下的内容

 

这其中比较有意思的是,如果你注册地图时,还有option.geo.map的名字用的是china,南海诸岛会如上图以缩略图展示,但是以此之外来命名地图,则不会展示缩略图。

再次声明,如果二者名字不一致,将会导致异常,致使地图无法显示

数据渲染
实际开发中,往往需要将后台的数据渲染到地图里,我们在option里添加series属性,以下是我的两个示例,仅做参考:

series: [
{
type: “scatter”,
coordinateSystem: “geo”,
symbol: “pin”,
legendHoverLink: true,
symbolSize: [60, 60],
// 这里渲染标志里的内容以及样式
label: {
show: true,
formatter(value) {
return value.data.value[2];
},
color: “#fff”,
},
// 标志的样式
itemStyle: {
normal: {
color: “rgba(255,0,0,.7)”,
shadowBlur: 2,
shadowColor: “D8BC37”,
},
},
// 数据格式,其中name,value是必要的,value的前两个值是数据点的经纬度,其他的数据格式可以自定义
// 至于如何展示,完全是靠上面的formatter来自己定义的
data: [
{ name: “西藏”, value: [91.23, 29.5, 2333] },
{ name: “黑龙江”, value: [128.03, 47.01, 1007] },
],
showEffectOn: “render”,
rippleEffect: {
brushType: “stroke”,
},
hoverAnimation: true,
zlevel: 1,
},
// {
// type: “effectScatter”,
// coordinateSystem: “geo”,
// effectType: “ripple”,
// showEffectOn: “render”,
// rippleEffect: {
// period: 10,
// scale: 10,
// brushType: “fill”,
// },

// hoverAnimation: true,
// itemStyle: {
// normal: {
// color: “rgba(255, 235, 59, .7)”,
// shadowBlur: 10,
// shadowColor: “#333”,
// },
// },
// zlevel: 1,
// data: [
// { name: “西藏”, value: [91.23, 29.5, 2333] },
// { name: “黑龙江”, value: [128.03, 47.01, 1007] },
// ],
// },
],

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
两种渲染方式如下:

 

 

使用备注的部分时,需要在option.tooltip里添加formatter属性,我写的如下:

const option = {
// …
tooltip: {
// …
formatter(params) {
return `地区:${params.name}</br>数值:${params.value[2]}`;
}
}
}
1
2
3
4
5
6
7
8
9
更多的方式还要自己多试验,这是个费时且需要耐心的活,你甚至可以将柱状图放上去。有更花里胡哨的效果,也请分享给我。

嵌入文字
使用option.graphic可以实现简单的水印效果

const option = {
// …
graphic:{
// 水印类型
type: ‘text’,
// 相对于容器的位置
left:’10%’,
top: ‘10%’,
// 样式设置
style: {
// 文本内容
text: “create by cRack_cLick”,
// 字体粗细、大小、字体
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
// 字体颜色
fill: “#fff”
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
效果如下:

 

利用graphic的type=“group”,还可以组合出一些有意思的效果(抄官方文档的效果):

graphic: {
type: “group”,
rotation: Math.PI / 4,
bounding: “raw”,
left: 110,
top: 110,
z: 100,
children: [
{
type: “rect”,
left: “center”,
top: “center”,
z: 100,
shape: {
width: 400,
height: 50,
},
style: {
fill: “rgba(0,0,0,0.3)”,
},
},
{
type: “text”,
left: “center”,
top: “center”,
z: 100,
style: {
fill: “#ddd”,
text: “create by cRack_cLick”,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
},
},
],
},

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

地图下钻
往往还有一种需求,在我们点击一个省的时候,需要切换到这个省的详细地图,甚至还可以下钻到市、县等等。

为了试下点击下钻,我们需要先了解echarts中的点击事件,文档参考

以目前的功能来说,我们暂时不需要加入其它的业务逻辑以及省级的数据渲染,仅仅只做地图的切换,所以点击事件里我们需要实现获取点击的省份名称,然后根据省份名称,来选择地图的JSON文件,最后重新渲染echarts图表,下面是我的简单示例:

// 新增加北京的地图JSON文件
import beijing from “@/assets/mapJson/data-beijing.json”;
// …

initCharts(){
const charts = echarts.init(this.$refs[“charts”]);
// …
// 注意这里是echarts的实例对象,而不是echarts组件本身。
charts.on(‘click’, ({name}) => {
if (name === “北京”) {
// 修改option的配置,可以继续自定义
option.geo.zoom = 0.8
// 就像上面提到的,这里必须要和注册地图时的名字一致
option.geo.map = “beijing”
// 注册地图
echarts.registerMap(“beijing”, beijing)
// 重新渲染
charts.setOption(option, true)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
需要注意的是,在重新setOption的时候,我们加入了第二个参数,按照官方文档的说法:

参数:

调用方式:

chart.setOption(option, notMerge, lazyUpdate);
1
或者

chart.setOption(option, {
notMerge: …,
lazyUpdate: …,
silent: …
});
1
2
3
4
5
option

图表的配置项和数据。

notMerge

可选,是否不跟之前设置的 option 进行合并,默认为 false,即合并。

lazyUpdate

可选,在设置完 option 后是否不立即更新图表,默认为 false,即立即更新。

silent

可选,阻止调用 setOption 时抛出事件,默认为 false,即抛出事件。

第二个从参数设置为true来让图表重新渲染,而不合并配置,当然,这一点具体需要看你显示开发的需求,我在这里仅是为了演示。绝不是偷懒

另外在echarts v3.x的版本里,切换地图默认是有过渡动画的,而v4.x和v5.x的版本里则没有过渡动画,如果知道怎么加上的,可以私信我。

上面虽然可以实现地图切换,但很显然开发中这么写要被打死。下钻三十多个地图要写三十多个if,显然是一种不理智的开发方式。一种方式我们可以通过axios或者ajax异步请求,但是这样需要你在生产环境和运维协商好,否则会导致请求不到JSON文件。

下面是我在前端写的一个简单的工具方法,仅供参考:

import zhongguo from “@/assets/mapJson/data-city.json”;
import neimenggu from “@/assets/mapJson/data-neimenggu.json”;
import beijing from “@/assets/mapJson/data-beijing.json”;
// …

const mapDict = {
“北京”: “beijing”,
“内蒙古”: “neimenggu”,
// …
}

const mapData = {
beijing,
neimenggu,
// …
}

export function getMap(mapName) {
const cityName = mapDict[mapName]
if(cityName){
return [cityName, mapData[cityName]]
}
return [‘china’, zhongguo]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
需要建立两个字典,一个是汉字和拼音的对照映射,一个是拼音和JSON文件的映射,这个可灵活配置,并非唯一。

优化一下上面的的代码:

// 删除地图json文件的引用,修改为上面的工具方法
import { getMap } from “./maputil”;

methods: {
initCharts() {
const charts = echarts.init(this.$refs[“charts”]);
const option = {
// …
};
// 不传name默认会返回中国地图
const [mapName, mapJson] = getMap();
option.geo.map = mapName;
// 地图注册,第一个参数的名字必须和option.geo.map一致
echarts.registerMap(mapName, mapJson);

charts.setOption(option);

charts.on(“click”, ({ name }) => {
// 这里和上面一样,其实还可以再优化一下。为了方便阅读,这里不再封装。
const [mapName, mapJson] = getMap(name);
option.geo.zoom = 0.8;
option.geo.map = mapName;
echarts.registerMap(mapName, mapJson);
charts.setOption(option, true);
});
}
}
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
效果如下:

 

结合水印制作级联效果
现在的地图可以下钻了,但是似乎操作起来还有些别扭。

我们现在想要的效果是:我们需要每下钻一层,水印部分就会加上当前地区的名称。点击水印地区的名称,就会跳转到当前地区的地图,我们要来改造一下echarts示例的click事件。

首先option.graphic的默认值修改为中国地图,这里为了方便阅读,仅使用text格式演示:

// …
graphic: [
{
type: “text”,
left: “10%”,
top: “10%”,
style: {
text: “中国”,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
fill: “#fff”,
},
},
],
1
2
3
4
5
6
7
8
9
10
11
12
13
以数组的形势编写后,思路就明显了,只要在click事件的时候,将下钻地图的信息push进来,并且为了防止重合,稍微移动一下定位即可,我的示例如下:

charts.on(“click”, ({ name }) => {
const [mapName, mapJson] = getMap(name);
option.geo.zoom = 0.8;
option.geo.map = mapName;
// 为了重新定位,这里使用了length
const idx = option.graphic.length + 1;
option.graphic.push({
type: “text”,
left: `${idx * 10}%`,
top: “10%”,
style: {
text: name,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
fill: “#fff”,
},
});
echarts.registerMap(mapName, mapJson);
charts.setOption(option, true);
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
点击后效果如下:

 

现在还有问题,就是点击地区名字没有响应,所以我们还要为option.graphic子元素加上click事件

这个click事件功能也类似,获取地图名称,获取地图数据,重新渲染。但是这个click事件需要注意,比如我点击了北京,那么在数组里是需要将密云区的元素删除掉的,同理,点击中国,则后面的元素都要删除。在这里我就不把相同的部分抽离出来了:

// 防止graph里频繁添加click事件,在添加click事件之前先全部清空掉。
charts.off()
charts.on(“click”, ({name}) => {
// 如果option.graphic里已经有了城市名称,则不进行任何操作,防止频繁点击
const index = option.graphic.findIndex(i => i.style.text === name);
if (!name || index !== -1) return
const [mapName, mapJson] = getMap(name);
option.geo.zoom = 0.8;
option.geo.map = mapName;
// 为了重新定位,这里使用了length
const idx = option.graphic.length + 1;
option.graphic.push({
type: “text”,
left: `${idx * 10}%`,
top: “10%”,
style: {
text: name,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
fill: “#fff”,
},
onclick: () => {
// 利用函数的作用域,可以直接拿上面的name来用
const [grahpName, graphJson] = getMap(name);
const index = option.graphic.findIndex(i => i.style.text === name);
// 点击元素之后的所有元素全部删除
option.graphic.splice(index + 1);
// 很多操作重复了,你可以将公共部分抽离出来
option.geo.map = mapName;
echarts.registerMap(grahpName, graphJson);
charts.setOption(option, true);
},
});
echarts.registerMap(mapName, mapJson);
charts.setOption(option, true);
});
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
这里会有个坑,在给graph添加click事件后,点击时会同时触发我们上面charts.on的click事件,想了很久也没有找到好一点的方式来解决这个事件冲突,最后只好判断了一下name是否为空来暂时解决。如果有更好的办法,也请留言。最终效果如下:

 

至此绘制地图已经完毕,更多是依靠自己的业务需求来进行更灵活的配置和渲染,它的API没有什么太复杂的,只是我们缺少了一点耐心去实验。

visualMap
首先来看效果

 

增加visualMap来让地图的数据渲染更有层次感,实现起来也很简单,只需要在option里增加visualMap配置即可:

const option = {
// …
visualMap: {
// 是否展示左下角,即是是false,也仅是不显示,不影响数据的映射
show: true,
// 上下端文字
text: [“高”, “低”],
// 最小值和最大值,必须指定
min: 0,
max: 6000,
// 位置
left: “10%”,
bottom: “10%”,
// 是否展示滑块
calculable: true,
// 指定映射的数据,对应的是option.series,这里根据自己的实际需要进行配置
seriesIndex: [0],
// 从下到上的颜色
inRange: {
color: [‘#00467F’, ‘#A5CC82’],
},
//字体颜色
textStyle: {
color: “#fff”,
map: “china”,
},
}
}

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
如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在option.series里再加一个元素,使其type=“map”,内容与geo一致即可,但是要多加data属性,渲染的数据和定位图标一致。并将seriesIndex的索引做好映射,即可实现。

const option = {
// …
visualMap: {
// 是否展示左下角,即是是false,也仅是不显示,不影响数据的映射
show: true,
// 上下端文字
text: [“高”, “低”],
// 最小值和最大值,必须指定
min: 0,
max: 6000,
// 位置
left: “10%”,
bottom: “10%”,
// 是否展示滑块
calculable: true,
// 指定映射的数据,对应的是option.series,这里根据自己的实际需要进行配置
seriesIndex: [0],
// 从下到上的颜色
inRange: {
color: [‘#00467F’, ‘#A5CC82’],
},
//字体颜色
textStyle: {
color: “#fff”,
map: “china”,
},
}
}
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
如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在option.series里再加一个元素,使其type=“map”,内容与geo一致即可,但是要多加data属性,渲染的数据和定位图标一致。并将seriesIndex的索引做好映射,即可实现。

如果出现了缩放重影,说明生成了两个地图组件,需要在新的series里加上geoIndex属性,值是geo里的索引,这样就只会共享一个组件,不会出现缩放重影的问题了

以下为源文档:

默认情况下,map series 会自己生成内部专用的 geo 组件。但是也可以用这个 geoIndex 指定一个 geo组件。这样的话,map 和 其他 series(例如散点图)就可以共享一个 geo组件了。并且,geo组件的颜色也可以被这个 map series 控制,从而用 visualMap来更改。

当设定了 geoIndex 后,series-map.map属性,以及 series-map.itemStyle 等样式配置不再起作用,而是采用 geo中的相应属性。

很多人找我要源码,博主的本意是希望大家可以自己实验,加深印象。就我个人来说,我拿到源码后就懒得去研究了,总觉得自己已经会了。但介于可能很多人还是刚刚接触,对很多内容并不熟悉,所以在这里贴一下源码,只不过因为时间久远,已经找不到写文章时的代码了,这里贴的是从项目里抽出来的代码,可能会有一些冗余,所以大家尽量参考文章来理解:源码地址
————————————————
版权声明:本文为CSDN博主「cRack_cLick」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SkelleBest/article/details/121204097

利用html 5 websocket做个山寨版web聊天室(手写C#服务器) - 谦行 - 博客园

mikel阅读(308)

来源: 利用html 5 websocket做个山寨版web聊天室(手写C#服务器) – 谦行 – 博客园

之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了websocket基本概念)等方式做一个山寨版的web聊天室。

什么是websocket

WebSocket 协议是html5引入的一种新的协议,其目的在于实现了浏览器与服务器全双工通信。看了上面链接的同学肯定对过去怎么低效率高消耗(轮询或comet)的做此事已经有所了解了,而在websocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。同时这么做有两个好处

1.通信传输字节减少:比起以前使用http传输数据,websocket传输的额外信息很少,据百度说只有2k

2.服务器可以主动向客户端推送消息,而不用客户端去查询

关于概念和好处,网上到处都是,不再赘述,简单看看其原理,然后动手写一个web版聊天室吧

握手

除了TCP连接的三次握手,websocket协议中客户端与服务器想建立连接需要一次额外的握手动作,在最新版的协议中是这个样子的

客户端向服务器发送请求

复制代码
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:8080
Origin: http://test.com
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: OtZtd55qBhJF2XLNDRgUMg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
复制代码

服务器给出响应

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=

在请求中的“Sec-WebSocket-Key”是随机的,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个魔幻字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端(来自维基百科)。

websocket API

经过握手之后浏览器与服务器建立连接,两者就可以互相通信了。websocket的API真心很简单,看看W3C的定义

复制代码
enum BinaryType { "blob", "arraybuffer" };
[Constructor(DOMString url, optional (DOMString or DOMString[]) protocols)]
interface WebSocket : EventTarget {
  readonly attribute DOMString url;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSING = 2;
  const unsigned short CLOSED = 3;
  readonly attribute unsigned short readyState;
  readonly attribute unsigned long bufferedAmount;

  // networking
           attribute EventHandler onopen;
           attribute EventHandler onerror;
           attribute EventHandler onclose;
  readonly attribute DOMString extensions;
  readonly attribute DOMString protocol;
  void close([Clamp] optional unsigned short code, optional DOMString reason);

  // messaging
           attribute EventHandler onmessage;
           attribute BinaryType binaryType;
  void send(DOMString data);
  void send(Blob data);
  void send(ArrayBuffer data);
  void send(ArrayBufferView data);
};
复制代码

创建websocket

ws=new WebSocket(address); //ws://127.0.0.1:8080

 

调用其构造函数,传入地址,就可以创建一个websocket了,值得注意的是地址协议得是ws/wss

关闭socket

ws.close();

 

调用webservice实例的close()方法就可以关闭webservice,当然也可以传入一个code和string说明为什么关了

几个回调函数句柄

由于其异步执行,回调函数自然少不了,有四个重要的

  • onopen:连接创建后调用
  • onmessage:接收到服务器消息后调用
  • onerror:出错时调用
  • onclose:关闭连接的时候调用

看名字就知道是干什么的了,每个回调函数都会传入一个Event对象,可以通过event.data访问消息

使用API

我们可以在创建socket成功后为其回调函数赋值

复制代码
ws=new WebSocket(address);
            ws.onopen=function(e){
                var msg=document.createElement('div');
                msg.style.color='#0f0';
                msg.innerHTML="Server > connection open.";
                msgContainer.appendChild(msg);
                ws.send('{<'+document.getElementById('name').value+'>}');
            };
复制代码

也可以通过事件绑定的方式

复制代码
ws=new WebSocket(address);
            ws.addEventListener('open',function(e){
                var msg=document.createElement('div');
                msg.style.color='#0f0';
                msg.innerHTML="Server > connection open.";
                msgContainer.appendChild(msg);
                ws.send('{<'+document.getElementById('name').value+'>}');
            });
复制代码

 客户端实现

其实客户端的实现比较简单,除了websocket相关的几句就是一些自动focus、回车键事件处理、消息框自动定位到底部等简单功能,不一一说明了

复制代码
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title>Web Socket Client</title>
 5 </head>
 6 <body style="padding:10px;">
 7     <h1>Web Socket Chating Room</h1>
 8     <div style="margin:5px 0px;">
 9         Address:
10         <div><input id="address" type="text" value="ws://127.0.0.1:8080" style="width:400px;"/></div>
11     </div>
12     <div style="margin:5px 0px;">
13         Name:
14         <div><input id="name" type="text" value="Byron" style="width:400px;"/></div>
15     </div>
16     <div>
17         <button id="connect" onclick="connect();">connect server</button> &nbsp;&nbsp;
18         <button id="disconnect" onclick="quit();">disconnect</button>&nbsp;&nbsp;
19         <button id="clear" onclick="clearMsg();">clear</button>
20     </div>
21     <h5 style="margin:4px 0px;">Message:</h5>
22     <div id="message" style="border:solid 1px #333; padding:4px; width:400px; overflow:auto;
23          background-color:#404040; height:300px; margin-bottom:8px; font-size:14px;">
24     </div>
25     <input id="text" type="text" onkeypress="enter(event);" style="width:340px"/> &nbsp;&nbsp;
26     <button id="send" onclick="send();">send</button>
27 
28     <script type="text/javascript">
29         var name=document.getElementById('name').value;
30         var msgContainer=document.getElementById('message');
31         var text=document.getElementById('text');
32 
33         function connect () {
34             var address=document.getElementById('address').value;
35             
36             ws=new WebSocket(address);
37             ws.onopen=function(e){
38                 var msg=document.createElement('div');
39                 msg.style.color='#0f0';
40                 msg.innerHTML="Server > connection open.";
41                 msgContainer.appendChild(msg);
42                 ws.send('{<'+document.getElementById('name').value+'>}');
43             };
44             ws.onmessage=function(e){
45                 var msg=document.createElement('div');
46                 msg.style.color='#fff';
47                 msg.innerHTML=e.data;
48                 msgContainer.appendChild(msg);
49             };
50             ws.onerror=function(e){
51                 var msg=document.createElement('div');
52                 msg.style.color='#0f0';
53                 msg.innerHTML='Server > '+e.data;
54                 msgContainer.appendChild(msg);
55             };
56             ws.onclose=function(e){
57                 var msg=document.createElement('div');
58                 msg.style.color='#0f0';
59                 msg.innerHTML="Server > connection closed by server.";
60                 msgContainer.appendChild(msg);
61             };
62             text.focus();
63         }
64 
65         function quit(){
66             if(ws){
67                 ws.close();
68                 var msg=document.createElement('div');
69                 msg.style.color='#0f0';
70                 msg.innerHTML='Server > connection closed.';
71                 msgContainer.appendChild(msg);
72                 ws=null;
73             }
74         }
75 
76         function send(){
77             ws.send(text.value);
78             setTimeout(function(){
79                 msgContainer.scrollTop=msgContainer.getBoundingClientRect().height;
80             },100);
81             text.value='';
82             text.focus();
83         }
84 
85         function clearMsg(){
86             msgContainer.innerHTML="";
87         }
88 
89         function enter(event){
90             if(event.keyCode==13){
91              send(); 
92             } 
93         }
94     </script>
95 </body>
96 </html>
复制代码

 

服务器支持

其实websocket的服务器实现网上可以找到很多了,向维基百科中列出的

但是我希望加入一些自己特定的处理,比如用户名识别啊、广播啊什么的,所以自己实现了一个简单的C#版本

websocket 聊天室C#服务器实现

我上篇博客C# socket编程实践——支持广播的简单socket服务器中介绍了怎么实现一个简单的C#广播服务器,在这上面扩展两个部分就能作为websocket服务器了

1.wecsocket握手处理

当服务器收到浏览器握手请求后,首先需要识别握手信息,我简单粗暴的使用了包含字符串处理,当然加入了是否握过手的判断

复制代码
client.BeginReceive (buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback (Recieve), client);
                string msg = Encoding.UTF8.GetString (buffer, 0, length);

                if (!clientPool [client].IsHandShaked && msg.Contains ("Sec-WebSocket-Key")) {
                    client.Send (PackageHandShakeData (buffer, length));
                    clientPool [client].IsHandShaked = true;
                    return;
                }
复制代码

 

识别成功后就可以生成服务器响应发送给客户端了

复制代码
/// <summary>
        /// 打包服务器握手数据
        /// </summary>
        /// <returns>The hand shake data.</returns>
        /// <param name="handShakeBytes">Hand shake bytes.</param>
        /// <param name="length">Length.</param>
        private byte[] PackageHandShakeData (byte[] handShakeBytes, int length)
        {
            string handShakeText = Encoding.UTF8.GetString (handShakeBytes, 0, length);
            string key = string.Empty;
            Regex reg = new Regex (@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = reg.Match (handShakeText);
            if (m.Value != "") {
                key = Regex.Replace (m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim ();
            }

            byte[] secKeyBytes = SHA1.Create ().ComputeHash (
                                     Encoding.ASCII.GetBytes (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            string secKey = Convert.ToBase64String (secKeyBytes);

            var responseBuilder = new StringBuilder ();
            responseBuilder.Append ("HTTP/1.1 101 Switching Protocols" + "\r\n");
            responseBuilder.Append ("Upgrade: websocket" + "\r\n");
            responseBuilder.Append ("Connection: Upgrade" + "\r\n");
            responseBuilder.Append ("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");

            return Encoding.UTF8.GetBytes (responseBuilder.ToString ());
        }
复制代码

 

2.解析浏览器消息和处理发送给浏览器的消息

websocket中数据并不完全是消息本身,我们需要做一些处理

复制代码
/// <summary>
        /// 解析客户端发送来的数据
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="recBytes">Rec bytes.</param>
        /// <param name="length">Length.</param>
        private string AnalyzeClientData (byte[] recBytes, int length)
        {
            if (length < 2) {
                return string.Empty;
            }

            bool fin = (recBytes [0] & 0x80) == 0x80; // 1bit,1表示最后一帧  
            if (!fin) {
                return string.Empty;// 超过一帧暂不处理 
            }

            bool mask_flag = (recBytes [1] & 0x80) == 0x80; // 是否包含掩码  
            if (!mask_flag) {
                return string.Empty;// 不包含掩码的暂不处理
            }

            int payload_len = recBytes [1] & 0x7F; // 数据长度  

            byte[] masks = new byte[4];
            byte[] payload_data;

            if (payload_len == 126) {
                Array.Copy (recBytes, 4, masks, 0, 4);
                payload_len = (UInt16)(recBytes [2] << 8 | recBytes [3]);
                payload_data = new byte[payload_len];
                Array.Copy (recBytes, 8, payload_data, 0, payload_len);

            } else if (payload_len == 127) {
                Array.Copy (recBytes, 10, masks, 0, 4);
                byte[] uInt64Bytes = new byte[8];
                for (int i = 0; i < 8; i++) {
                    uInt64Bytes [i] = recBytes [9 - i];
                }
                UInt64 len = BitConverter.ToUInt64 (uInt64Bytes, 0);

                payload_data = new byte[len];
                for (UInt64 i = 0; i < len; i++) {
                    payload_data [i] = recBytes [i + 14];
                }
            } else {
                Array.Copy (recBytes, 2, masks, 0, 4);
                payload_data = new byte[payload_len];
                Array.Copy (recBytes, 6, payload_data, 0, payload_len);

            }

            for (var i = 0; i < payload_len; i++) {
                payload_data [i] = (byte)(payload_data [i] ^ masks [i % 4]);
            }

            return Encoding.UTF8.GetString (payload_data);
        }
复制代码

 

复制代码
/// <summary>
        /// 把客户端消息打包处理(拼接上谁什么时候发的什么消息)
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="message">Message.</param>
        private byte[] PackageServerData (SocketMessage sm)
        {
            StringBuilder msg = new StringBuilder ();
            if (!sm.isLoginMessage) { //消息是login信息
                msg.AppendFormat ("{0} @ {1}:\r\n    ", sm.Client.Name, sm.Time.ToShortTimeString ());
                msg.Append (sm.Message);
            } else { //处理普通消息
                msg.AppendFormat ("{0} login @ {1}", sm.Client.Name, sm.Time.ToShortTimeString ());
            }


            byte[] content = null;
            byte[] temp = Encoding.UTF8.GetBytes (msg.ToString ());

            if (temp.Length < 126) {
                content = new byte[temp.Length + 2];
                content [0] = 0x81;
                content [1] = (byte)temp.Length;
                Array.Copy (temp, 0, content, 2, temp.Length);
            } else if (temp.Length < 0xFFFF) {
                content = new byte[temp.Length + 4];
                content [0] = 0x81;
                content [1] = 126;
                content [2] = (byte)(temp.Length & 0xFF);
                content [3] = (byte)(temp.Length >> 8 & 0xFF);
                Array.Copy (temp, 0, content, 4, temp.Length);
            } else {
                // 暂不处理超长内容  
            }

            return content;
        }
复制代码

 

如果看不懂我在写什么,并且对C#版websocket感兴趣的同学需要看一下我上一篇博客了

有图有真相

看看我们写的聊天室运行效果吧

 服务器

 客户端——Byron

客户端——Casper

源代码

源代码我已经上传到了我的github上,对github不感冒的同学可以直接点击这里下载,有兴趣的同学可以下载看看,因为我在Mac+mono+Xamarin Studio上开发的(忽然感觉自己好极品在Mac上用C#。。。),所以代码编码和Windows可能会有些差距,改成Windows兼容的应该就可以直接运行了,不过没在Window下跑过,如果有错误还请大家多多指正。

C#版Websocket实例 - Seaurl - 博客园

mikel阅读(311)

来源: C#版Websocket实例 – Seaurl – 博客园

Demo地址:www.awbeci.xyz

websocket有java、nodejs、python,Php等等版本,我使用的是C#版本,服务器端是Fleck,github地址:https://github.com/statianzo/Fleck

这篇博客就是引用上面的一个例子教你如何使用客户端和服务器端来使用websocket的,对于英文还不错的同学,直接看上面 的源代码就可以了,下面开始讲解如何使用:

在说之前我们先来看看哪些浏览器支持websocket:

WebSocket客户端支持
浏览器 支持情况
Chrome Chrome version 4+支持
Firefox Firefox version 5+支持
IE IE version 10+支持
Safari IOS 5+支持
Android Brower Android 4.5+支持
WebSocket 服务端支持
厂商 应用服务器 备注
IBM WebSphere WebSphere 8.0 以上版本支持,7.X 之前版本结合 MQTT 支持类似的 HTTP 长连接
甲骨文 WebLogic WebLogic 12c 支持,11g 及 10g 版本通过 HTTP Publish 支持类似的 HTTP 长连接
微软 IIS IIS 7.0+支持
Apache Tomcat Tomcat 7.0.5+支持,7.0.2X 及 7.0.3X 通过自定义 API 支持
Jetty Jetty 7.0+支持

 

现在我们看看浏览器端如何实现:

复制代码
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>websocket client</title>
    <script type="text/javascript">
        var start = function () {
            var inc = document.getElementById('incomming');
            var wsImpl = window.WebSocket || window.MozWebSocket;
            var form = document.getElementById('sendForm');
            var input = document.getElementById('sendText');

            inc.innerHTML += "connecting to server ..<br/>";

            // create a new websocket and connect
            window.ws = new wsImpl('ws://localhost:7181/');

            // when data is comming from the server, this metod is called
            ws.onmessage = function (evt) {
                inc.innerHTML += evt.data + '<br/>';
            };

            // when the connection is established, this method is called
            ws.onopen = function () {
                inc.innerHTML += '.. connection open<br/>';
            };

            // when the connection is closed, this method is called
            ws.onclose = function () {
                inc.innerHTML += '.. connection closed<br/>';
            }

            form.addEventListener('submit', function (e) {
                e.preventDefault();
                var val = input.value;
                ws.send(val);
                input.value = "";
            });

        }
        window.onload = start;
    </script>
</head>
<body>
    <form id="sendForm">
        <input id="sendText" placeholder="Text to send" />
    </form>
    <pre id="incomming"></pre>
</body>
</html>
复制代码

服务器端我使用的是C#的控制台程序:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fleck;

namespace TopoWebSocket
{
    class Program
    {
        static void Main(string[] args)
        {
            FleckLog.Level = LogLevel.Debug;
            var allSockets = new List<IWebSocketConnection>();
            var server = new WebSocketServer("ws://0.0.0.0:7181");
            server.Start(socket =>
            {
                socket.OnOpen = () =>
                {
                    Console.WriteLine("Open!");
                    allSockets.Add(socket);
                };
                socket.OnClose = () =>
                {
                    Console.WriteLine("Close!");
                    allSockets.Remove(socket);
                };
                socket.OnMessage = message =>
                {
                    Console.WriteLine(message);
                    allSockets.ToList().ForEach(s => s.Send("Echo: " + message));
                };
            });


            var input = Console.ReadLine();
            while (input != "exit")
            {
                foreach (var socket in allSockets.ToList())
                {
                    socket.Send(input);
                }
                input = Console.ReadLine();
            }
        }
    }
}
复制代码

注意要引用:using Fleck;这个在上面的链接里面已经提供了。代码都写好后,我们来运行下服务器端吧,客户端也可以,总之两个都运行好:如图:

现在你可以客户端向服务器端发送消息了,我们试试,输入你好世界:

然后我们再在服务器端发送消息给浏览器试试,如,HelloWorld

也成功了,呵呵,是不是很神奇,现在可以添加自己 的业务逻辑在里面了,是不是 觉得前后台通信变得简单了。

总结:

1、这个例子不是我写的,我只不过在github上拿过来给你们介绍,你们可以通过上面的例子fork下源代码,自己看看分析分析别人是怎么用的

2、如果有问题可以加群问我:464696550

3、参考资料:http://www.ibm.com/developerworks/cn/java/j-lo-WebSocket/

一文读懂什么是iPaaS - 知乎

mikel阅读(414)

来源: 一文读懂什么是iPaaS – 知乎

#什么是iPaaS?

iPaaS是Intergration Platform as a Service(集成平台即服务)的简写。Gartner将其定义为“促进开发、执行和集成流治理同任何本地以及基于云的流程、服务、应用和数据连接的一套云服务,可以在独立的或者多个交叉的组织中进行”。简而言之,iPaaS是一种基于云的自助服务解决方案,将应用程序的集成方式标准化。从而使开发人员、技术顾问甚至非技术人员都可以使用iPaaS快速构建集成流程,实现企业内数据互联互通

#企业集成方式的演变

伴随着企业信息化程度的提升,导致集成方式的不断变化,最终演变出了iPaaS。起初,企业通过自开发或采购第三方系统实现公司相关业务业务。然而各系统彼此孤立,用途单一,如同一根根独立的烟囱,无法联通。而后,企业通过自定义编程去实现点对点的应用打通。这种方式简单有效,但随着应用系统的增多,点对点的集成会形成蛛网般的布局,从而导致运维压力剧增,且集成能力无法复用。由此,集成方式便走向了ESB(企业服务总线)。各业务应用系统通过ESB提供的适配器,实现内部数据的服务化进行集成,不仅解决了“烟囱”式系统之间的数据孤岛问题,同时还能够集中企业所有的服务资产,在统一的平台中进行管理和监控,大幅提高了服务的共享和复用,避免了点对点集成方式中由于系统之间的集成情况不清晰,导致重复投入集成资源“重复造轮子”的事情发生,降低了集成成本的同时,基于ESB提供的开箱即用的应用适配器,也提高了集成服务的开发效率,一定程度上解决了企业集成运维的压力;随着微服务技术和云应用的推广,企业数字化进程中,系统架构和环境在不断演变,传统的内部网路环境应用系统之间的集成基本是能够通过ESB满足,但是企业网络边界不断在扩大,比如云上云下集成、混合云集成、多工厂/多基地集成等,传统ESB采用的集中式的部署架构则已经无法满足了。为了满足企业多基地多工厂的数据联通要求,集成方式又演化出分布式集成。分布式集成基于企业内网,实现跨集团与分子公司间对接。然而这种方式,也会产生较高的安全风险,给IT团队带来巨大的管理成本。时至今日,随着微服务架构以及容器技术的推出,由一系列基于微服务架构的轻量级API集成组件、数据集成组件、消息集成组件、业务系统链接器等组成的iPaaS平台便应运而生。

#iPaaS的工作原理

iPaaS为企业中所有应用程序之间的数据集成提供了一套工具和统一的流程,无论这些数据存储在本地还是云端,企业仅需要选择所需的工具和服务进行可视化的配置即可。iPaaS作为一种云计算技术,主要由平台服务、工具和服务三部分组成,可以让企业轻松地在云中部署、运行和管理应用程序集成解决方案。

#iPaaS的优点

使用iPaaS有很多好处,包括:

1.开箱即用:

运营&开发人员可以自助使用iPaaS,通过可视化的编排页面,快速实现不同应用程序的集成。iPaaS提供省时省力的的智能工具,帮助用户在短时间内完成丰富的集成场景,大大提高了企业IT部门的生产力。

可视化流程编排

2.高扩展性:

iPaaS可以根据企业的集成需求的增长,轻松实现扩展。iPaaS支持企业自定义应用服务连接,帮助企业实现应用系统的快速接入。

技术与应用连接器

3.更高的灵活性:

iPaaS平台使得企业应用集成具有了更高的灵活性,可以快速适应企业不断变化的业务需求。从需求提出到开发完成,测试上线,原先以日为单位的开发周期缩短至以小时或分钟为单位,IT部门可以根据业务的调整,随时随地调整应用间的集成方式。

API在线调试
API一键发布

4.更高效的运维监控:

iPaaS通常除了提供应用集成相关服务外,也会提供相应的运维服务,例如接口预警、接口日志等基础功能,可以帮助运维人员随时了解集成情况,出问题后也可以快速定位并予以解决。

调用日志
预警策略

5.更高的安全性:

iPaaS可以通过提供对数据源和数据仓库访问的集中控制,搭配网关服务,可以针对集成接口进行认证、流量、频次、加密等限制,来帮助IT部门保护数据。

API策略
网关插件

6.更低的集成成本:

iPaaS解决方案通常比自定义集成、基于消息集成和企业集成项目(如ESB的实施)便宜得多。大多采用订阅式的服务,从而使得采购成本大幅降低,使得越来越多的小型企业进行应用集成成为可能。

#iPaaS产品如何选择

企业选择iPaaS产品时,需要考虑以下因素:

  • 易用性:该iPaaS是否易于使用,提供的应用连接器是否丰富,是否使用低代码平台或无代码方式,从而无需专业技术人员便可以实现应用集成。
  • 安全性:该iPaaS平台是否支持对数据访问进行集中控制,提供数据加密、身份认证等一系列网关插件,从而有效保护企业数据资产。
  • 稳定性:iPaaS底层架构是否高可用,能否支持企业所需数量级的数据请求,是否具有完善的容灾备份机制。从而防止影响企业正常生产运营。
  • 需要的功能:企业需要明确自己所必需的功能,如数据转换、工作流自动化或SaaS应用接入等功能是否支持。
  • 预算和运维支持:企业需要根据自己的预算,选择合适的iPaaS平台于付费模式,与此同时,需要考察iPaaS供应商提供的运维支持服务,是否包含技术支持、使用培训、协助推广和业务咨询等。

#得帆云DeFusion iPaaS融合集成平台

得帆云DeFusion iPaaS融合集成平台是融合了企业集成常用的数据集成ETL、应用集成ESB、能力开放API三个核心引擎为一体的企业级集成平台,基于分布式架构和云原生特性,DeFusion iPaaS融合集成平台可以帮助企业轻松实现云应用的集成打通,在其高安全的统一集成能力保障下,让企业无需担心不同环境下网络连通的安全风险,通过得帆云DeFusion iPaaS融合集成平台提供的丰富且可以自定义扩展的应用连接器,简单快速且高效地实现混合环境应用之间的数据共享和功能集成。

得帆云DeFusion iPaaS融合集成平台对企业集成API的资产汇总、全生命周期管理、监控预警通知、API门户等企业集成管理必须的功能也做了增强,全面提高企业集成管理能力;再结合得帆丰富的集成实施经验和实施能力,为企业提供产品+实施的全方位集成解决方案。得帆云DeFusion iPaaS融合集成平台产品旨在为企业重新定义集成,降低企业集成成本,提升集成效率,在企业面临强大内外部压力下而不得不支撑快速变化的前端需求时,为企业构建底层支撑能力。

(1)海量系统协议接入:得帆云DeFusion iPaaS融合集成平台提供300+的应用系统、技术协议层面的连接器,为企业提供各类业务系统、各类底层协议的连接支撑。连接器可以通过Java+图形化方式进行快速扩展,方便企业根据自身需求进行定制,全面提升企业对接能力,从应用(Application)、企业(Business)、云(Cloud)、设备(Device)几个领域,将企业需要的能力集中接入,集中管理。

(2)云应用集成:随着企业系统上云、各类SaaS应用的使用,企业的网络环境变得多样化,得帆云DeFusion iPaaS融合平台能够让企业轻松应用云上环境、云下本地环境、私有化环境、同城异地机房等环境的下应用的集成,同时提供了各类SaaS原生的连接器,实现SaaS应用之间、以及SaaS与本地应用系统之间的集成,并有效保障不同云应用之间的集成数据一致性。

(3)API资产全面管理:得帆云DeFusion iPaaS融合平台致力于承载企业全域集成API管理,对于不是通过平台运行的API类型,如:EDI、其他企业级ESB平台API等,也可以将API资产接入,实现资产的大融合。通过平台的API信息管理、分类、标签等能力,全面维护API资产,同时也可以通过报表工具展示企业全量的系统集成关系,方便企业API资产的留存和关系的梳理。

(4)运行过程深度监控:所有在平台中运行的API ,都经过平台的全面深度监控 ,对于API运行的步骤、传输时间、报文大小、报文详情等均可以进行监控查看,为企业提供一站式仲裁平台,杜绝系统对接时的“推诿“行为,明确指出集成问题所在,快速定位,提升IT效率。同时,平台也提供预警通知功能,对于API、平台、系统层级的问题,及时给出全渠道的预警或通知,进一步提升集成的稳定性和对接效率。

(5)企业能力开放支撑:平台提供完整的企业级API网关能力,以支撑企业的能力开放,对底层业务系统提供功能和数据的API化;对数据中台、数据仓库以及其他数据的汇聚中心,平台也提供数据即服务(DaaS)功能,将数据模型、数据对象快速生成API。API接入API网关后,可以基于网关进行API的限制和保护,提供安全验证、鉴权、限流、熔断等相关管理功能。

(6)清晰便捷API门户:得帆云DeFusion iPaaS融合平台为企业提供强大的API门户功能,对接入平台的API,可以开放在API门户中,通过API门户实现对API的外网开放。企业可以基于API门户,实现对API的开放同时,也实现对API的权限管控(流程化审批授权),计数计费,文档发布等管理和监控。全面降低企业与合作伙伴、C端用户的对接成本,提升对接效率。

 

 

docker入门加实战—Docker镜像和Dockerfile语法 - 随机的未知 - 博客园

mikel阅读(321)

来源: docker入门加实战—Docker镜像和Dockerfile语法 – 随机的未知 – 博客园

镜像

镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程。

镜像结构

我们要从0部署一个Java应用,大概流程是这样:

  1. 准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
  2. 安装并配置JDK
  3. 拷贝jar包
  4. 配置启动脚本

镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。

例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:

Java项目镜像结构

Dockerfile

由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。

而这种记录镜像结构的文件就称为Dockerfile,其对应的语法可以参考官方文档:

https://docs.docker.com/engine/reference/builder/

其中的语法比较多,比较常用的有:

指令 说明 示例
FROM 指定基础镜像 FROM centos:7
ENV 设置环境变量,可在后面指令使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY ./xx.jar /tmp/app.jar
RUN 执行Linux的shell命令,一般是安装过程的命令 RUN yum install gcc
EXPOSE 指定容器运行时监听的端口,是给镜像使用者看的;
但是后面还是需要-p来做端口映射的
EXPOSE 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 ENTRYPOINT java -jar xx.jar

例如,要基于Ubuntu镜像来构建一个Java应用,其Dockerfile内容如下:

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
ENV TZ=Asia/Shanghai
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 设定时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装JDK
RUN cd $JAVA_DIR \
 && tar -xf ./jdk8.tar.gz \
 && mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 指定项目监听的端口
EXPOSE 8080
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

以后我们会有很多java项目需要打包为镜像,它们都需要Linux系统环境、JDK环境这两层,只有上面的3层不同(因为jar包不同)。如果每次制作java镜像都重复制作前两层镜像,是很麻烦的。

所以,就有人提供了基础的系统加JDK环境,我们在此基础上制作java镜像,就可以省去JDK的配置了:

FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

构建镜像

在/root/demo目录下有如下两个文件

目录文件

其中docker-demo.jar是Java项目;Dockerfile如下:

# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

然后,执行命令,构建镜像:

# 进入镜像目录
cd /root/demo
# 开始构建
docker build -t docker-demo:1.0 .
  • docker build: 就是构建一个docker镜像
  • -t docker-demo:1.0 :-t参数是指定镜像的名称(repositorytag
  • . : 最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.代表当前目录,也可以直接指定Dockerfile目录:
    • # 直接指定Dockerfile目录
      docker build -t docker-demo:1.0 /root/demo
      

如果没有指定Dockerfile的名字,就必须叫Dockerfile。

运行结果如下:

运行结果

查看镜像

查看镜像

可以看到多了一个docker-demo的镜像;

尝试运行:

docker run -d --name dd -p 8080:8080 docker-demo:1.0

运行结果

.Net核心级的性能优化(GC篇) - 江湖评谈 - 博客园

mikel阅读(337)

来源: .Net核心级的性能优化(GC篇) – 江湖评谈 – 博客园

1.前言

大部分人对于.Net性能优化,都停留在业务层面。或者简单的.Net框架配置层面。本篇来看下.Net核心部分GC垃圾回收配置:保留VM,大对象,独立GC,节省内存等.Net8里面有很多的各种GC配置,用以帮助你的程序进行最大程度性能提升和优化。

文章分为两部分,第一个是GC有哪些动作可以性能最大的优化,第二部分就是如何配置这些动作以便让你的程序达到这些性能效果。

2.GC动作

1.节省内存:System.GC.ConserveMemory(默认值为0)

2.独立GC:DOTNET_GCName(默认值,coreclr.dll的默认GC)

3.大型对象堆阈值DOTNET_GCLOHThreshold(默认值85000字节,指定值必须大于其)

4.允许大型对象DOTNET_gcAllowVeryLargeObjects(默认垃圾回收大于2GB的数组,也就是默认值为1)

5.大型页面DOTNET_GCLargePages,指示堆硬限制是否使用大型页面 DOTNET_GCLargePages(默认值为0,也即是不使用)

6.保留VM,DOTNET_GCRetainVM。意思是把一个经过垃圾回收,没有对象的段是被系统回收还是放在堆段列表,以便下次使用.(默认值false,被系统回收)

7.高内存百分比DOTNET_GCHighMemPercent,当物理内存负载大于其指定的值的时候,进行完整的垃圾回收,以便腾出跟多空间(默认负载阈值介于90%到 97%之间)

8.对象堆限制百分比

DOTNET_GCHeapHardLimitSOHPercent,DOTNET_GCHeapHardLimitLOHPercent,DOTNET_GCHeapHardLimitPOHPercent.分别为小对象堆,大对象堆,固定堆的限制百分比。

9.对象堆限制

DOTNET_GCHeapHardLimitSOH,DOTNET_GCHeapHardLimitLOH,DOTNET_GCHeapHardLimitPOH可以根据每个对象堆指定 GC 的允许堆使用量

10.堆限制百分比DOTNET_GCHeapHardLimitPercent默认值(仅在某些情况下适用)是20MB或容器内存限制的75%(以较大者为准)

11.堆限制DOTNET_GCHeapHardLimit默认值(仅在某些情况下适用)是20MB或容器内存限制的75%(以较大者为准)

12.关联DOTNET_GCNoAffinitize指定是否将垃圾回收线程与处理器关联。若要关联一个 GC 线程,则意味着它只能在其特定的 CPU 上运行。为每个 GC 线程创建一个堆。

13.CPU组DOTNET_GCCpuGroup,配置垃圾回收器是否使用CPU组。(默认值为0,表示不会跨CPU组)

14.关联范围DOTNET_GCHeapAffinitizeRanges,指定用于垃圾回收器线程的处理器列表。以逗号分隔的处理器编号列表或处理器编号范围。
Unix 示例:“1-10,12,50-52,70”
Windows 示例:“0:1-10,0:12,1:50-52,1:70”

15.关联掩码DOTNET_GCHeapAffinitizeMask,指定垃圾回收器线程应使用的确切处理器数。

16.堆计数DOTNET_GCHeapCount限制通过垃圾回收器创建的堆数。

3.配置

配置这些GC设置,主要是可以从三个方面来配置。
其一:运行时配置文件runtimeconfig.json
以保留VM为例:

{
   "runtimeOptions": {
      "configProperties": {
         "System.GC.RetainVM": true
      }
   }
}

其二:MSbuild配置
以保留VM为例:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <RetainVMGarbageCollection>true</RetainVMGarbageCollection>
  </PropertyGroup>
</Project>

其三:环境变量配置

Windows CMD:
set DOTNET_GCRetainVM=true

Windows Powershell:
$env:DOTNET_GCRetainVM=true

Unix/Linux
export DOTNET_GCRetainVM=true
BASH 复制 全屏

以上16个GC动作均可以通过这个三个配置方式来进行配置。

参考微软官网:

https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector