深度优先

这个家伙好懒,除了文章什么都没留下

0%

原文地址:https://www.cnblogs.com/han-1034683568/p/6941337.html

本文提要

本文目的不仅仅是创建一个MySQL的镜像,而是在其基础上再实现启动过程中自动导入数据及数据库用户的权限设置,并且在新创建出来的容器里自动启动MySQL服务接受外部连接,主要是通过Dockerfile和shell脚本实现这一过程。
至于这么做的原因可以看一下这篇文章 《将数据的初始化放到docker中的整个工作过程(问题记录)》,为了实现和docker-compose整合,试了很多种方法都没法实现需求,最终是通过这种方法才解决掉问题。

搭建步骤

1、首先创建Dckerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM mysql:5.7

#设置免密登录
ENV MYSQL_ALLOW_EMPTY_PASSWORD yes

#将所需文件放到容器中
COPY setup.sh /mysql/setup.sh
COPY schema.sql /mysql/schema.sql
COPY privileges.sql /mysql/privileges.sql

#设置容器启动时执行的命令
CMD ["sh", "/mysql/setup.sh"]

2、编写容器启动脚本setup.sh:

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
#!/bin/bash
set -e

#查看mysql服务的状态,方便调试,这条语句可以删除
echo `service mysql status`

echo '1.启动mysql....'
#启动mysql
service mysql start
sleep 3
echo `service mysql status`

echo '2.开始导入数据....'
#导入数据
mysql < /mysql/schema.sql
echo '3.导入数据完毕....'

sleep 3
echo `service mysql status`

#重新设置mysql密码
echo '4.开始修改密码....'
mysql < /mysql/privileges.sql
echo '5.修改密码完毕....'

#sleep 3
echo `service mysql status`
echo 'mysql容器启动完毕,且数据导入成功'

tail -f /dev/null

这里是先导入数据,然后才是设置用户和权限,是因为mysql容器一开始为免密登录,Dockerfile中有如下设置:ENV MYSQL_ALLOW_EMPTY_PASSWORD yes,此时执行导入数据命令不需要登录验证操作,如果是先执行权限操作,那么导入数据则需要登录验证,整个过程就麻烦了许多。

3、需要导入数据的mysql脚本命令schema.sql:

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

-- 创建数据库
create database `docker_mysql` default character set utf8 collate utf8_general_ci;

use docker_mysql;

-- 建表
DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
`id` bigint(20) NOT NULL,
`created_at` bigint(40) DEFAULT NULL,
`last_modified` bigint(40) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- 插入数据
INSERT INTO `user` (`id`, `created_at`, `last_modified`, `email`, `first_name`, `last_name`, `username`)
VALUES
(0,1490257904,1490257904,'john.doe@example.com','John','Doe','user');

因为是测试,所以随便写了一个建表语句,如果是真实项目肯定不止这一张表,直接将建表语句覆盖过来就好。

4、mysql权限设置命令privileges.sql:

1
2
3
4
5
6
7
8
9
use mysql;
select host, user from user;
-- 因为mysql版本是5.7,因此新建用户为如下命令:
create user docker identified by '123456';
-- 将docker_mysql数据库的权限授权给创建的docker用户,密码为123456:
grant all on docker_mysql.* to docker@'%' identified by '123456' with grant option;
-- 这一条命令一定要有:
flush privileges;

5、创建镜像

docker build -t 13/docker-mysql .

docker build 为创建镜像命令,名称为13/docker-mysql,’.’表示当前目录,即Dockerfile文件所在的目录,创建过程如下:

执行docker images查看该镜像是否存在于镜像列表中:

创建成功。

6、启动容器

docker run -d -p 13306:3306 13/docker-mysql
启动容器,并将端口映射到本地的13306端口,命令行如图所示:

容器启动成功。
查看容器的日志记录,启动过程与启动脚本setup.sh中所规范的步骤一致,数据导入和权限设置成功:

验证结果

1、通过进入容器在命令行验证

启动时容器的id为9db491b1d760,因此执行exec命令进入容器:
docker exec -it 9db491b1d760 /bin/bash
这个命令不要直接使用,因为在你机器上id可能不同,替换掉id值即可。

前文中创建了docker_mysql数据库,并在此数据库中创建了user表,同时将数据库的连接授权赋予了新建的docker用户,因此验证过程为:

  • 使用docker用户登录数据库:mysql -u docker -p

  • 输入密码123456通过登录验证

  • 切换至docker_mysql数据库:use docker_mysql;

  • 查看数据库中的表:show tables;

  • 查看表中的数据:select * from user;

整个过程如下:

通过图中的结果对比,与前文一致,验证成功。

2、通过mysql客户端管理软件验证

通过图中的结果对比,与前文一致,验证成功。

https://steemit.com/utopian-io/@cha0s0000/arduino-sg90
https://www.cnblogs.com/zhanggaoxing/p/11748663.html
https://github.com/dotnet/iot/blob/master/src/devices/ServoMotor/README.md

一、认识SG90舵机模块及应用

舵机,也叫做伺服马达,内部控制系统是具有闭环控制系统的机电结构。

舵机由以下部件组成:

  • 外壳
  • 电路板
  • 无核心马达
  • 齿轮
  • 位置检测器

SG90舵机模块的的原理主要是由核心闭环控制系统发出PWM(脉冲宽度调制)信号给舵机,然后信号在电路板上得到IC处理之后计算出转动的角度, 根据设定的角度驱动无核心马达转动,通过减速齿轮给摆臂以动力,在此同时电位器返回当前的位置信号,判断是否已经到达设定位置。

SG90舵机模块的特性如下:

  • 只能旋转180度
  • 尺寸:22.3 X 11.8 X 26.3 mm
  • 操作速度:0.12秒/60度(4.8V);0.1秒/60度(6.0V)(无负载条件下)
  • 扭矩大小:1.3kg·cm(4.8V);1.5kg·cm(6.0V)
  • 操作温度:-30~+60°C
  • 正常工作电压:4.8V~6V

二、编写程序驱动SG90舵机模块转动

连接Arudino UNO与SG90舵机模块

SG90舵机模块出厂时配有三条不同颜色的接线已连接在舵机控制电路上,分别是:

  • 棕色 : 接地线

  • 红色 : 接电源正极线

  • 橙色 : 模块信号输出引脚

Arduino UNO SG90舵机模块
GND 棕色接线
5V 红色接线
D10 橙色接线
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
using Iot.Device.ServoMotor;
using Iot.Device.Uln2003;
using System;
using System.Device.Pwm;
using System.Threading;

namespace MyServoMotor
{
internal class Program
{
private static void Main(string[] args)
{
ServoMotor servoMotor = new ServoMotor(PwmChannel.Create(0, 0));

servoMotor.Start(); // Enable control signal.
while (true)
{
//for (int i = 0; i < 180; i++)
//{
// servoMotor.WriteAngle(i);
// Thread.Sleep(15 * 1);
//}
//for (int i = 180; i >= 1; i--)
//{
// servoMotor.WriteAngle(i);
// Thread.Sleep(15 * 1);
//}
servoMotor.WriteAngle(0);
Thread.Sleep(100 * 1);
servoMotor.WriteAngle(90);
Thread.Sleep(100 * 1);
servoMotor.WriteAngle(180);
Thread.Sleep(100 * 1);
servoMotor.WriteAngle(90);
Thread.Sleep(100 * 1);
Console.WriteLine(DateTime.Now);
}
//servoMotor.Stop();
}
}
}

如何启用 Raspberry Pi 上的 PWM ?

修改 /boot/config.txt ,添加 dtoverlay=pwm 。

启用 PWM 通道 1 请参考:https://github.com/raspberrypi/firmware/issues/1178

修改 GPIO 引脚功能请参考:https://www.dummies.com/computers/raspberry-pi/raspberry-pi-gpio-pin-alternate-functionshttp://abyz.me.uk/rpi/pigpio/pigs.html

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
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details
# dtoverlay=pwm
# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan
disable_overscan=0

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
hdmi_group=2
hdmi_mode=82

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

[pi4]
# Enable DRM VC4 V3D driver on top of the dispmanx display stack
dtoverlay=vc4-fkms-v3d
max_framebuffers=2


[all]
#dtoverlay=vc4-fkms-v3d

audio_pwm_mode=2
dtoverlay=audremap,pins_18_19
dtoverlay=pwm-2chan,pin=18,func=2,pin2=19,func2=2
dtoverlay=vga666-6
start_x=1
gpu_mem=128
enable_uart=1
dtoverlay=w1-gpio

https://www.cnblogs.com/tynam/p/13611709.html

概念

索引器(Indexer) 允许类中的对象可以像数组那样方便、直观的被引用。当为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array) 一样。
索引器可以有参数列表,且只能作用在实例对象上,而不能在类上直接作用。
可以使用数组访问运算符([ ])来访问该类的实例。
索引器的行为的声明在某种程度上类似于属性(property)。属性可使用 get 和 set 访问器来定义索引器。但是属性返回或设置的是一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。

定义一个一维数组的索引器:

1
2
3
4
5
6
7
8
9
10
11
12
13
element-type this[int index]
{
// get 访问器
get
{
// 返回 index 指定的值
}
// set 访问器
set
{
// 设置 index 指定的值
}
}

提示:索引器必须以this关键字定义,this 是类实例化之后的对象

实例:

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
using System;

namespace C_Pro
{
public class Student
{
private string name;
private string grade;
public string Name
{
get {return name; }
set {name = value; }
}
public string Grade
{
get {return grade; }
set {grade = value; }
}
// 定义索引器
public string this[int index]
{
get
{
if (index == 0) return name;
else if (index == 1) return grade;
else return null;
}
set
{
if (index == 0) name = value;
else if (index == 1) grade = value;
}
}
static void Main(string[] args)
{
Student s = new Student();
s[0] = "Jeson";
s[1] = "First-year";
Console.WriteLine(s.Name);
Console.WriteLine(s.Grade);
Console.ReadKey();
}
}
}

输出:

1
2
3
Jeson

First-year

重载索引器

索引器(Indexer)可被重载。索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型。没有必要让索引器必须是整型的。CSharp 允许索引器可以是其他类型,例如,字符串类型。

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
using System;

namespace C_Pro
{
public class IndexedNames
{
private string[] namelist = {"a", "b", "c", "d"};
// 输入namelist的index返回对应的值
public string this[int index]
{
get
{
return namelist[index];
}
set
{
namelist[index] = value;
}
}
// 输入namelist的值,返回对应的索引
public int this[string name]
{
get
{
for (int i=0; i<namelist.Length; i++)
{
if (namelist[i] == name) return i;
}
return -1;
}
}

static void Main(string[] args)
{
IndexedNames name = new IndexedNames();
Console.WriteLine(name[1]);
Console.WriteLine(name["a"]);
}
}
}

运行后结果:

1
2
b
0

索引器与数组的区别:

  • 索引器的索引值(Index)类型不限定为整数,用来访问数组的索引值(Index)一定为整数,而索引器的索引值类型可以定义为其他类型。
  • 索引器允许重载, 一个类不限定为只能定义一个索引器,只要索引器的函数签名不同,就可以定义多个索引器,可以重载它的功能。
  • 索引器不是一个变量,索引器没有直接定义数据存储的地方,而数组有。索引器具有Get和Set访问器。

索引器与属性的区别:

  • 索引器以函数签名方式 this 来标识,而属性采用名称来标识,名称可以任意。
  • 索引器可以重载,而属性不能重载。
  • 索引器不能用static 来进行声明,而属性可以。索引器永远属于实例成员,因此不能声明为static。