首页> 标签> 容器
"容器"
共 54007 条结果
全部 问答 文章 公开课 课程 电子书 技术圈 体验
Java-String类&集合ArrayList
java.lang.String类代表字符串。Java程序中所有字符串文字都为此类的对象。注意:String类首字母大写,类的首字母都大写。以"xx"形式给出的字符串对象,在字符常量池中存储。字符串类型,可以定义字符串变量指向字符串对象。string变量每次的修改都是产生并指向了新的字符串对象。原来的字符串对象都是没有改变的,所以称不可变字符串。创建字符串方法一:直接使用""创建String name = "张三";方法二:使用String()构造器String name = new String();使用字符数组:String name = new String(char[] c);char[] chars={'你','好','世','界'}; String str=new String(chars); System.out.println(str);//你好世界使用字节数组:String name = new String(byte[] c);byte[] bytes = {65, 66, 'C', 'C' + 1}; String str = new String(bytes); System.out.println(str);//ABCD两种创造方式的区别以""方式给出的字符串对象,在字符串常量池中储存,而且相同内容只会在其中存储一份。通过构造器new对象,每new一次都会产生一个新对象,放在堆内存中。举个例子//创建了两个对象 String s1 = new String("abc"); //创建了0个对象 String s2 = "abc"; //输出false System.out.println(s1 == s2);第一行代码,分别在字符串常量池和堆区创建了一个"abc"对象。第二行代码,指向了字符串常量池中已有的"abc"对象。第三行代码,两个String对象存储的地址不一样,输出falseJava存在编译优化机制String s1 = "abc"; String s2 = "ab"; String s3 = s2 + "c"; String s4 = "ab" + "c"; //false System.out.println(s1 == s3); //true System.out.println(s1 == s4);程序在编译阶段,"ab"+"c"会直接转成"abc"。字符串内容比较直接使用==会出现问题直接使用==对比的是字符串对象存储的地址。//正确的用户名密码 String name = "name"; String password = "password"; //用户输入用户名密码 Scanner sc = new Scanner(System.in); String tmpname = sc.next(); String tmppsword = sc.next(); //判断用户输入是否正确 System.out.println(name == tmpname);//false System.out.println(password == tmppsword);//false比较内容要用.equals()方法。//正确的用户名密码 String name = "name"; String password = "password"; //用户输入用户名密码 Scanner sc = new Scanner(System.in); String tmpname = sc.next(); String tmppsword = sc.next(); //判断用户输入是否正确 System.out.println(name.equals(tmpname)); System.out.println(password.equals(tmppsword));.equalsIgnoreCase()可以忽略大小写比较,常用于对比验证码。常用API通过.lenth()和.charAt()遍历字符串获取字符串长度.lenth()获取某个索引位置处的字符.charAt()String str="你好世界"; for (int i=0;i<str.length();i++) { System.out.println(str.charAt(i)); }将字符串转化为字符数组Java中字符数组可以直接输出,也可以用下标访问输出。String str = "你好世界"; char[] chars = str.toCharArray(); System.out.println(chars); for (int i = 0; i < str.length(); i++) { System.out.println(chars[i]); }截取内容,包前不包后很多语言的方法,用到区间时,都是:包前不包后String str = "Java是世界上最好的编程语言"; //Java System.out.println(str.substring(0,4)); char[] chars=str.substring(4).toCharArray(); //是世界上最好的编程语言 System.out.println(chars);替换关键词String str = "金三胖是最厉害的,金三胖胖胖的,我好喜欢金三胖"; String str1 = str.replace("金三胖", "XX"); System.out.println(str1);判断是否包含关键词String str = "金三胖是最厉害的,金三胖胖胖的,我好喜欢金三胖"; System.out.println(str.contains("金三胖"));//true判断以什么开始String str = "金三胖是最厉害的,金三胖胖胖的,我好喜欢金三胖"; System.out.println(str.startsWith("金三胖"));//true System.out.println(str.startsWith("金二胖"));//false分割字符串,以字符串数组返回String name = "kunkun,蔡徐坤,ikun"; String[] names = name.split(","); for (String str : names) { System.out.println(str); }String案例验证码定义一个String类型的变量,存储a-z,A-Z,0-9之间的全部字符。循环五次,随机一个范围内的索引,获取对应字符连接起来即可。//定义可能出现的字符信息 String datas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; //循环五次,每次生成一个随机的索引,提取对应的字符连接起来 String code = ""; Random rd = new Random(); for (int i = 0; i < 5; i++) { //随机一个索引 int index = rd.nextInt(datas.length()); code += datas.charAt(index); } //输出字符串变量 System.out.println(code);模拟用户登录功能系统后台定义好正确的登录名称、密码。使用循环控制三次,让用户输入正确的登录名和密码,判断是否登录成功,登陆成功则不再进行登录,登陆失败则给出提示,并让用户继续登录。//定义正确的用户名密码 String okLoginName = "Caixukun"; String okPassword = "ctraplq"; //定义一个循环,让用户登录 Scanner sc = new Scanner(System.in); for (int i = 0; i < 3; i++) { System.out.println("请输入用户名"); String loginName = sc.next(); System.out.println("请输入密码"); String password = sc.next(); //判断登陆是否成功 if (loginName.equals(okLoginName)) { //判断密码是否正确 if (password.equals(okPassword)) { System.out.println("登陆成功"); break; } else System.out.println("密码错误,还剩" + (3 - i) + "次机会"); } else System.out.println("用户名错误"); }手机号码屏蔽以字符串形式从键盘接受一个手机号,将中间四位号码屏蔽,最终效果为:183****3572分析:键盘录入一个字符串。截取字符串前三位,截取字符串后四位。将截取后的两个字符串,中间加上**进行拼接,输出结果即可。//键盘录入一个手机号码 Scanner sc = new Scanner(System.in); System.out.println("请输入手机号码"); String tel = sc.next(); //截取代码前三位、后四位 String before = tel.substring(0, 3); String after = tel.substring(7); String s = before + "****" + after; System.out.println(s);集合ArrayList集合与数组类似,是一种容器,用于装数据。数组的特点:数组定义完成并启动后,类型确定,长度固定。问题:在个数不确定,且要进行增删数据操作时,数组是不太合适的。集合的特点:集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合非常适合做元素个数不确定,且要进行增删操作的业务场景。集合提供了许多丰富好用的功能,而数组的功能很单一。ArrayList集合的对象添加与获取ArrayList是集合的一种,它支持索引。.add()返回值为boolean,一般不会添加失败,因此我们一般不会注意它的返回值。System.out.println(list);会直接输出集合内容,而不是地址//创建ArrayList集合对象 ArrayList list = new ArrayList(); //添加数据 list.add("java"); list.add(1); list.add(2.5); list.add(false); System.out.println(list.add('可')); System.out.println(list);//[java, 1, 2.5, false, 可] //在指定索引位置插入元素 list.add(1, "插入"); System.out.println(list);//[java, 插入, 1, 2.5, false, 可]泛型概述ArrayList<E>就是一个泛型,可以在编译阶段约束集合对象,只能操作某种数据类型。使用泛型:<数据类型>举例:ArrayList<String>:此集合只能操作字符串类型的元素。ArrayList<Integer>:此集合只能操作整数类型的元素。注意:集合中只能存储引用类型,不支持基本数据类型。ArrayList<Integer>不能填<int>。代码规范:使用泛型来定义和使用集合ArrayList<String> list = new ArrayList<String>(); //JDK1.7开始,泛型后面的类型声明可以不写 ArrayList<String> list1 = new ArrayList<>(); //使用Object存储所有数据类型 ArrayList<Object> list2 = new ArrayList<>();ArrayList常用API、遍历.get()//获取某个索引位置处的元素值.size()//获取集合大小.remove()//删除某个索引位置处的值,并返回被删除的值.set()//修改某个索引位置的值,会返回修改前的值ArrayList<Integer> list = new ArrayList<>(); list.add(2); list.add(4); list.add(6); list.add(2); //获取某个索引位置处的元素值 System.out.println(list.get(1)); //获取集合大小 System.out.println(list.size()); //完成集合的遍历 for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i) + " "); } //删除某个索引位置处的值,并返回被删除的值 System.out.println("\n" + list.remove(1)); System.out.println(list); //直接删除元素值,删除成功返回true,失败返回false //需要使用Integer,否则是删除索引 System.out.println(list.remove((Integer) 2)); System.out.println(list); //修改某个索引位置的值,会返回修改前的值 System.out.println(list.set(0, 2)); System.out.println(list);ArrayList存储自定义数据类型的对象Movie m1 = new Movie("流浪地球", 9.0, "吴京"); //创建一个Movie类型的ArrayList,存储电影对象 ArrayList<Movie> arrayList = new ArrayList<>(); arrayList.add(m1); arrayList.add(new Movie("熊出没", 9.9, "光头强")); //遍历每一个对象,访问信息 //直接输出自定义数据类型,结果是地址 System.out.println(arrayList); for (int i = 0; i < arrayList.size(); i++) { System.out.println(arrayList.get(i).getName() + " " + arrayList.get(i).getScore() + " " + arrayList.get(i).getActor()); }Movie类文件:public class Movie { private String name; private double score; private String actor; public Movie(String name, double score, String actor) { this.name = name; this.score = score; this.actor = actor; } public String getName() { return name; } public double getScore() { return score; } public String getActor() { return actor; } }学生信息系统的数据搜索需求:后台程序需要存储学生信息并展示,然后要提供按照学号搜索的功能。分析:定义Student类,定义ArrayList存储学生对象信息,并遍历展示出来。提供一个方法,可以接收ArrayList集合,和要搜索的学号,返回搜索到的学生对象信息,并展示。使用死循环,让用户可以不停的搜索。Student类public class Student { private String studyNumber; private String name; private int age; public Student(String studyNumber, String name, int age) { this.studyNumber = studyNumber; this.name = name; this.age = age; } public String getStudyNumber() { return studyNumber; } public String getName() { return name; } public int getAge() { return age; } } Main类import java.util.ArrayList; import java.util.Scanner; public class Main { public static void main(String[] args) { //创建集合存储学生对象,创建学生对象封装学生信息 ArrayList<Student> studentArrayList = new ArrayList<>(); studentArrayList.add(new Student("001", "张三", 10)); studentArrayList.add(new Student("002", "李四", 12)); studentArrayList.add(new Student("003", "王五", 11)); //遍历学生信息展示 for (int i = 0; i < studentArrayList.size(); i++) { Student tmp = studentArrayList.get(i); System.out.println(tmp.getStudyNumber() + "\t" + tmp.getName() + "\t" + tmp.getAge()); } //定义方法,完成按照学号搜索的功能 Scanner sc = new Scanner(System.in); while (true) { System.out.println("请输入学号"); String studyNumber = sc.next(); //调用方法查询 Student student = getStudentById(studentArrayList, studyNumber); if (student == null) { System.out.println("查无此人"); } else System.out.println(student.getStudyNumber() + "\t" + student.getName() + "\t" + student.getAge()); } } /** * 根据学生学号,查询学生对象返回 * * @param a 存储全部学生对象的集合 * @param studentNumber 搜索的学生的学号 * @return 学生对象 | null */ public static Student getStudentById(ArrayList<Student> a, String studentNumber) { //遍历全部学生对象 for (int i = 0; i < a.size(); i++) { //询问当前遍历的这个学生对象的学号,是否是我们要找的学号 Student s = a.get(i); if (s.getStudyNumber().equals(studentNumber)) { //找到了 return s; } } //没找到 return null; } }
文章
存储  ·  Java  ·  API  ·  数据安全/隐私保护  ·  对象存储  ·  索引  ·  容器
2023-03-27
DIV是干什么的?底层原理是什么?
是HTML标记语言中的一个块级元素,它是一种通用容器,可以用来将HTML文档中的内容组织成逻辑区块,从而方便应用CSS样式和JavaScript行为。使用可以将页面分成多个部分,每个部分可以独立设置样式、布局和事件处理。 底层原理如下: 解析HTML文档:当浏览器加载HTML文档时,它会解析文档中的所有标记,并将它们转换成一棵DOM树。在这个过程中,浏览器会将标记转换成一个DOM元素节点。 创建DOM元素:当浏览器解析到标记时,它会创建一个DOM元素节点,并将其添加到文档树中。这个DOM元素节点代表了一个独立的区块,可以包含其他HTML标记、文本、图片、表格等内容。 应用CSS样式:使用可以方便地组织文档内容,从而使得应用CSS样式更加灵活。通过为添加class、id等属性,可以针对不同的区块设置不同的样式,从而实现页面的布局和美化。 添加JavaScript行为:除了样式和布局,还可以用来添加JavaScript事件处理程序。通过为添加事件处理函数,可以在用户与页面进行交互时触发相应的行为,例如弹出对话框、更新数据、跳转页面等。 总之,是HTML中最常用的块级元素之一,它可以将HTML文档中的内容组织成逻辑区块,从而方便应用CSS样式和JavaScript行为。的底层原理是将其解析为一个DOM元素节点,并将其添加到文档树中,从而构建出整个HTML文档的结构。
文章
JavaScript  ·  前端开发  ·  容器
2023-03-27
CentOS7.9下的docker实战应用
1 背景基于阿里云 云服务平台ESC的项目实践,使用centos操作系统2 docker2.1 docker安装docker安装可参考 阿里云社区->控制台(右上角)->容器镜像服务(左上角三道杠,然后搜索)->镜像加速 即可看到安装教程容器镜像服务链接:容器镜像服务链接2.1.1 安装/升级Docker客户端方法(1)使用官方脚本自动安装(仅适用于公网环境)curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun方法(2)手动安装# Step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-manager --add-repo http://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo # Step 3: 更新并安装 Docker-CE yum makecache fast yum -y install docker-ce # Step 4: 开启Docker服务 service docker start2.1.2 配置加速镜像配置镜像加速器针对Docker客户端版本大于 1.10.0 的用户您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器mkdir -p /etc/docker tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://20frrjos.mirror.aliyuncs.com"] } EOF systemctl daemon-reload systemctl restart docker2.2 docker基本使用2.2.1 案例1 直接拉取docker镜像启动docker运维可视化(1) 查询docker镜像库的可用版本docker search portainer(2)拉取可用版本docker pull portainner/portainner(3) 查看拉取镜像docker images(4)运行容器docker run -d -p 9000:9000 --name portainer_test 5f11注:-d表示后台运行 -p表示端口映射 --name便是容器命名 最后参数为 imagesID钱4位(5)查看运行容器docker ps -a(5)浏览器访问url:9000注: url:表示ESC公网地址 9000位docker run时指定的端口2.2.2 案例2 .jar+dockerfile打包启动通过docker启动自己的jar包(1)编写dockerfilevi dockerfiledockerfile内容:#拉取jdk8作为基础镜像 FROM java:8 #作者 MAINTAINER rowangao <gaochl@126.com> # 添加jar到镜像并命名为user.jar ADD user-0.9.1-SNAPSHOT.jar user.jar #镜像启动后暴露的端口 EXPOSE 8080 # jar运行命令,参数使用逗号隔开 ENTRYPOINT ["java","-jar","user.jar"](2)build建立docker镜像docker build -f dockerFile -t spring_new .注:-f 表示加载的dockerFile文件 -t表示生成目标名(也就是镜像名字) 不要忘记最后还有一个点 .(3)查看刚生成的镜像docker images(4)启动容器docker run -d -p 8080:8080 --name srping_test e4f3与2.2.1的最后docker run的格式相同 (5)查看运行容器docker ps -a(6)浏览器访问url:8001注: url:表示ESC公网地址 8001位docker run时指定的端口
文章
运维  ·  数据可视化  ·  Java  ·  Linux  ·  Shell  ·  Docker  ·  容器
2023-03-27
云原生学习(四)
(一)docker redis 主从配置1.创建文件夹mkdir /root/data/redis/master/data touch /root/data/redis/master/redis.conf mkdir /root/data/redis/slave-1/data touch /root/data/redis/slave-1/redis.conf 2.yum install redis redis-server -y 将/etc/redis.conf 复制到修改bind 0.0.0.0 daemonize no requirepass 123456 appendonly yes(从节点修改 masterauth 123456 replicaof 172.17.0.2 6379 )3.运行容器docker run -p 16379:6379 --name redis-master -v /root/data/redis/master/data/:/data -v /root/data/redis/master/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf docker run -p 26379:6379 --name redis-slave-1 -v /root/data/redis/slave-1/data/:/data -v /root/data/redis/slave-1/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf 4.验证docker exec -it redis-master redis-cli role info replication5.验证不成功docker exec -it redis-slave redis-cli slaveof 172.17.0.2 6379------------------------------docker部署docker 搭建修改为清华源2 yum install -y yum-utils device-mapper-persistent-data lvm2 3 yum-config-manager --add-repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo 4 yum repolist 5 yum -y install docker-ce 6 systemctl start docker 7 systemctl status docker 8 docker -v 10 cat /etc/docker/daemon.json{"registry-mirrors":["https://mirrors.tuna.tsinghua.edu.cn/"]}(二)docker容器部署docker search portainerdocker pull 6053537/portainer-ce docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock 6053537/portainer-ce(三)解决bin-log 1)log-bin=mysql-bin 2)set global binlog_format=ROW; 3)systemctl restart mariadb
文章
Cloud Native  ·  NoSQL  ·  关系型数据库  ·  Redis  ·  Docker  ·  容器
2023-03-27
MySQL的表空间是干什么的?底层原理是什么?
在 MySQL 中,表空间(Tablespace)是一个逻辑概念,它是用来管理存储在硬盘上的表数据和索引数据的容器。每个 MySQL 表都有一个对应的表空间,表空间包含一个或多个数据文件,这些数据文件保存了表的数据和索引。MySQL 使用表空间来管理磁盘上的数据文件,表空间在磁盘上的文件可以是单个文件或者是多个文件。表空间的大小是由其中的数据文件的大小总和决定的。当表空间中的数据文件不足以存储表数据时,MySQL 可以自动地扩展表空间,增加一个或多个新的数据文件,以满足表数据的存储需求。表空间在 MySQL 中的底层实现方式是通过文件系统来实现的。每个表空间都被映射到一个或多个文件,在文件系统中以文件的形式存在。这些文件通常位于操作系统的文件系统中的特定目录下,该目录通常称为数据目录。不同的存储引擎对表空间的实现方式可能会有所不同。例如,InnoDB 存储引擎使用了多版本并发控制(MVCC)来实现事务隔离,因此在 InnoDB 中,表空间包含了多个版本的数据,以支持并发访问。而 MyISAM 存储引擎则将表空间划分为多个数据文件和索引文件,以提高性能。总之,MySQL 的表空间是一个重要的概念,它是用来管理存储在硬盘上的表数据和索引数据的容器,通过文件系统来实现。在不同的存储引擎中,表空间的实现方式可能会有所不同,但都遵循相同的基本原理。
文章
存储  ·  关系型数据库  ·  MySQL  ·  索引  ·  容器
2023-03-27
如何高效搭建资产管理平台?众安科技告诉你答案是图技术
本⽂整理⾃ NebulaGraph x 阿⾥云计算巢专场中众安保险的⼤数据应⽤⾼级专家曾⼒带来的《众安资产在 NebulaGraph 的应⽤实践》分享,视频⻅链接。⼤家好,我是众安数据科学应⽤中⼼的曾⼒,今天很⾼兴在这⾥可以跟⼤家分享 NebulaGraph 在众安资产的实践。01 基于事件的数据资产平台设计在了解这⼀切之前,我们需要先知道什么是资产管理平台以及它可以解决什么样的问题。资产管理平台是全域的元数据中⼼,它可以对数据资产进行管理监控,解决企业内部的数据孤岛问题,挖掘数据价值并对业务赋能。它主要解决我们数据找不到、数据从哪⼉取,排查路径⻓、数据复⽤率低这四个非常核⼼的关键问题。设计目标对于资产管理平台,我们有三个⾮常重要的设计⽬标——强扩展:是指实体关系定义、资产操作以及资产查询的扩展性。低耦合:是指资产平台与其他系统对接时,对接入系统业务流程零影响。高时效:是指需要近实时的数据采集、快速的数据处理和查询性能。核心功能数据资产管理平台核⼼功能包括以下三个:类型定义:需提供⼀个抽象的设计定义不同的实体/关系,以及它们包含的属性。每个定义的实体/关系均需要定义唯一性约束,用于数据判重。在此基础上我们可以扩展一些定义类型,比如标签、术语、标签传播等等。元数据采集:主要有通过周期性、流式和手工录入三种方式进行数据采集。元数据管理:数据存储常见的选型是关系型数库存储定义或数据,搜索引擎存储数据、变动记录、统计类信息,图数据库则负责关系查询。数据分析常见的场景是数据地图、血缘及影响性分析、全链路血缘分析。数据应用则是在相关数据采集到平台后,可以快速实现资产割接、数据安全管理以及数据治理等更高层次应用需求。类型定义开源系统 Apache Atlas借鉴于开源系统 Apache Atlas 和 DataHub,我们来初步了解类型定义设计的核心要素。Atlas 的类型定义模式是一套基于 JSON 的 TypeSystem,可以自定义扩展,它的核心概念是实体、关系和属性,并在此基础上扩展出分类、术语、业务数据等定义设计。DataHub 则采用 Avro 进行事件模型的定义、PEGASUS 建模语言进行实体、关系和属性的建模,值得一提的是 Aspect 这个概念,其描述实体特定方面的属性集合,同一实体关联的的多个 Aspect 可以独立更新,相同的 Aspect 也可以再多个实体间共享。DataHub 预置了一些实体和关系模型,我们可以复用这些模式或自定义新模型。通过两个开源系统的类型定义设计,我们不难看出实体、关系、属性是元数据系统当中最基础的三个核心类型定义的元素。基于整体的架构、内部数据模型场景、数据存储选型、学习成本等方面因素的考虑,众安数据资产平台的类型定义是参照 Apache Atlas 的 TypeSystem 设计,定义一套独立的类型定义系统。实体类型定义 EntityDef 的核心要素是类型名称、父类型名称和属性列表。对于类型名称,需要单租户下约束唯一;对于父类型名称,其实就是对一些公共属性集的复用,类似于 Java 类的继承机制,我们可以通过获取父类型及其超类的所有属性。目前为方便类型解析,一个实体仅能定义一个父类型。对于属性列表,一个实体可以有 1~n 个属性,且至少有一个唯一性属性。关系定义 RelationshipDef 的核心要素是定义名称、关系类别、起始/结束端点定义和属性定义;对于类型名称,需要单租户下约束唯一;对于关系类别,根据是否容器关系和端点实体生命周期分为三类。Association 关系:是一种非容器关系,比较典型的例子是调度作业的依赖关系,两者之间不为包含关系,且生命周期独立。Aggregation 关系:是一种容器关系,但端点实体的生命周期独立,比如我们的报表系统,数据模型和画布关系,画布包含模型,但模型可以独立于画布而出存在,生命周期独立。Composition 关系:是一种容器关系,且端点生命周期完全一致,最直观的例子是表和列之间的包含关系,删除表的时候列实体自动被删除。对于端点定义 RelationshipEndDef,端点即是实体关系中关系实体的映射,所以需要定义来源和目标两个端点。每个端点定义需要端点的实体类型名称以及是否为容器。如果关系类别是⼀个容器类型的关系的话,需要设置某⼀个端点容器标志为 true,此时边方向是子项实体指向容器实体。如果关系类别是非容器的关系的话,所有的端点容器标志都需要设置为 false,此时边方向是端点 1 实体指向端点 2 实体。对于属性列表来,一个关系可以有 0~n 个属性。同实体属性定义不同的是,关系定义可以不配置属性定义。属性定义 AttributeDef 核心要素是名称、类型、是否可选、是否唯一属性、是否创建索引、默认值等内容。对于属性类型,因 NebulaGraph 图库支持的类型有限,仅支持基础数据类型。是否支持索引创建,是指创 Nebula tag/edge schema 的时候,对于某个属性是否创建一个 tag/edge 索引,以支持在特殊查询场景下的数据查询。实体的判重是资产平台类型定义的关键设计,我们首先看看开源产品的设计理念。Atlas 类型定义系统当中,所有实体都继承于⼀个⽗实体 Referenceable,它只有⼀个唯一属性 QualifiedName,且被标记为了唯⼀的属性。所有继承于它的实体类型属性中均没有唯一属性。QualifiedName 没有用固定格式,在 Atlas 内置的几个 Hook 中,主要格式为 xxx@meta-namespace。在 Hook 写入时指定,上图的例子就定义的是某个集群、某个存储卷在的唯一性标识。DataHub 实体唯一性标志是 URN,也叫作唯⼀属性资源名称。它有一定的生成规则,即 urn:<namspace>:<Entity Type>:<ID> 命名空间默认设置为 li,类别则是实体定义名称,ID 是指唯一属性集合拼接,可以嵌套 URN,上图的例子一个数据集,代表某个 Kafka 集群下的 Topic。基于两个开源项目分析,不难看出唯一性判断均是基于唯一属性来处理,两者均是在 Ingest 扩展中进行了固定格式的定义写入,而不是基于实体定义中多个明确代表唯一属性进行灵活的拼接处理,其拼接的字段晦涩难以解析。众安设计了一套唯一性判断定义方式,即某个实体注册时,先判断实体定义是否有 Composition 类别关系的边定义引用。如果不存在该关系类别定义,则直接从实体定义的属性定义中检索 isUnique=true 的属性。如果存在改关系类别定义,那当前实体的唯一性属性将不足以约束其唯一性,还需要带上边定义的容器实体的唯一属性才可以保证。这是一个递归的过程,可能需要传入多个实体的唯一性属性才可以判断。比如注册一个 MySQL 表,除了表实体的表名称之外,还需要 MySQL 库实体的 Host、端口、数据库名称等唯一属性才是完整的为唯一属性列表。在获取了唯一属性列表后,还需要加上租户和类型定义名称,继而生成某一租户下对应的唯一实体标志。操作需要三个流程,首先需要把唯⼀性的属性列表,根据其对应的类型名称跟属性名称进行一次正序排序,然后对租户、类型定义名称、唯一属性 key 进行一次正序排序,生成一个可读性高的唯一名称。其次,因唯一名称可能较长,需要进行一次 32 位摘要后进行存储,并加以索引进行查询,可以提升整体查询的有效性。最终全局的资产唯一 ID,则是用 Snowflake 算法生成的唯一 ID。因摘要算法有效概率重复,故使用分布式 ID 生成算法生成 ID,用于数据存储。资产采集流式采集有非常好的优势,可以通过消息队列,实现系统间解耦,实现数据的准实时上报,同时对事件消息也有良好的扩展性。周期性采集是流式采集的⼀个补充,它包括两种⽅式基于 ETL 或系统接口的主动推送,或类似数据发现系统的数据主动拉取。对于以上两种⽅式还没有达成的采集,可以用过人工补录的形式进行填写,以支持注入对接系统无法支持上报或部分血缘无法解析等场景,提升数据完整度。下面给大家介绍一下众安元数据系统⼏个版本采集流程的迭代——V1.0 版本是完全基于 T+1 的离线 ETL,我们会把数据开发⼯作台、调度系统以及阿⾥云 MaxCompute 元数据加载到数仓后,通过 ETL 处理推送到元数据平台,因数据量不大一个支持递归的关系型数据库即可满足要求。若数据量较大,则可以通过搜索引擎和图数据库进行扩展。随着业务的发展,数据开发对于元数据的时效性要求会越来越高,比如分析师创建的临时数据想 T0 就直接分享给其他部门使用,以及元数据整体数量越来越大,处理耗时较长,获取的时间越来越晚。基于以上需求,我们在元数据平台开了⼀层 API,在数据开发工作台进行表操作时,或调度系统创建调度任务时,会调用接口将数据同步给元数据平台。同时晚上我们依然会有离线的 ETL 进行数据补充,两者结合起来进行数据源的数据查询服务。接口模式也会有一定的弊端,在各个对接的业务系统中,会有大量的同步嵌套流程,元数据服务不可用或执行时间过长,例如系统发版时的业务中断,创建一个数百列的表引发的接口超时等,均会影响正常业务流程。于是我们参考各类开源元数据平台设计思路,设计了基于流式事件的元数据平台,基于不同的事件,对接系统通过消息队列上报后,实现系统间解耦。资产平台基于不同事件进行分类处理,并将最终的数据存储到搜索引擎、关系型数据库,以及图数据库当中。平台架构下⾯给⼤家介绍⼀下众安数据资产平台的架构,我们将平台分为了 5 个子系统。Portal 服务对接前端,提供通用的实体页面布局配置接口,实现配置化的页面布局。同时转发请求到 Core Service 进行处理,比如查询、类型定义等。Discovery 服务主要就是周期性的采集服务,通过配置定时的采集任务,并实现元数据的定时采集。系统 SDK 所有服务对接资产平台,均需要通过 SDK 进行对接,包括 Discovery 服务、数据超市、报表平台、开发⼯作台、数据标签平台等,SDK 提供了统一的事件拼装、权限管理、事件推送等功能,可以极大的提升平台间交互的开发效率。Event 服务负责消费消息队列中的消息,进行事件的解析和数据持久化。Core 服务提供统一的查询 API、标签 API 以及类型定义的 API 来实现查询跟类型定义的管理。同时我们提供了统一的数据存储层模块 Repo,来实现查询器和统一数据处理器的相关处理,其内部提供了数据库及图库的扩展 SPI,以便实现相关扩展。我们将资产平台的事件抽象为以下三种:元数据事件 MetadataEvent,包括实体/关系的增删改查等子事件。元数据异常事件 FailMetadataEvent,在处理 MetadataEvent 时失败了,比如类型定义不存在或事件顺序有问题,我们会统一生成一个元数据异常失败事件,可以基于此事件做异常数据落库或告警通知。平台事件 PlatformEvent,包括使用元数据平台触发的埋点事件,包括实体的收藏、查询、使用以及安全分级等事件,其中一部分会做按天级别的统计处理,以便在平台上可以看到类似的统计信息。事件进⾏处理,需要关注以下三点:分而治之,因为整体的事件的数据量会⽐较多,为了保证性能需要按照 Event 类别和影响,使⽤不同的消息队列。对于我们刚才介绍的三种型的事件,我们实际使用了三个 Kafka Topic 进行消息推送。消息的顺序,对于元数据相关事件,消息消费需要严格保证有序,如何来保证有序呢?我们⽬前采⽤的⽅案是由 Kafka Topic 单分区模式来解决的,为什么不⽤多 Partition 呢?因为实体跟关系之间的注册有可能是会分到不同的 Partition 上来进⾏处理的,因为异步消费处理有可能不同分区的数据产生消费堆积,有概率出现不同的分区,消费注册事件先到,实体注册事件后到的情况,导致废消息的出现。最终一致性,因为事件 Event 的异步处理,我们只能保证数据的最终⼀致性。好,那讲完了事件的消费流程,我们下⾯就要来看数据持久化的流程。我们的数据事件从消息队列拿到之后,会被我们的事件服务 Event Service 所消费,Event Service 中的事件处理器在消费数据的时候会⽴刻对这个数据进⾏⼀份数据的存储,它会存到关系型数据库⾥⾯,作为⼀个审计的回溯⽇志。在存储完回复⽇志之后,事件处理器就会开始对事件进⾏处理,如果事件处理异常的话,根据特定的这种事件类型,我们会有选择的把⼀些异常的事件放到异常事件的消息队列⾥⾯,然后供下游的系统进⾏订阅通知,或者是做内部后期的问题排查。如果事件处理成功了之后,我们会把数据丢到联合数据处理器当中。那联合数据处理器内部其实就是我们对关系型数据库以及图库的数据进⾏了⼀个整体的事务的包裹,以便两者之间出现失败的时候,可以对数据内容进⾏回滚。那在数据持久化当中,我们的关系型数据库跟图库当中分别存储了什么内容呢?像关系型数据库当中,我们往往存储了实体跟关系的数据,包括属性跟这种实际的这种名称的⼀些定义,同时还存储了实体的统计类的信息⽤于分析,还有类型定义的数据⽤于各种各样数据的这样⼀种校验。那图库当中主要就是点边的这种关系⽤于图谱的查询。资产的查询分析集成于 Core Service 模块中,目前有两大场景分类,数据地图和血缘分析。数据地图类检索,一般是分查询,我们定义一套类似于 ES DSL 风格的查询接口请求,通过查询解析器,翻译成要查询的关系型数据库语句,目前因为数据量还在PG的承受范围内,我们并没有使用 ES。同时使用、收藏、查询的统计类记录和变动记录,也是存放于 PG 当中,通过指定接口查询。血缘分析类查询,一般是关系查询,我们也通过类似于 ES DSL 风格的查询接口请求,通过查询解析器,翻译成图数据库所识别的 nGQL 或 Cypher 语句,包括 N 跳关系查询、子图查询、属性查询等。对于⼀些特殊场景查询需求,比如数据大盘,或特定实体的扩展事件,我们通过或定制化查询的方式进行处理。02 NebulaGraph 在众安资产平台的实践图数据库选型我们在做⾃主化平台开发之前,对热门开源项目的图数据库选型做了调研。选型主要考虑两⽅⾯的因素,数据库架构和资产平台设计的匹配性。在架构因素⽅⾯,核心因素是读写性能、分布式扩展、事务支持和第三方依赖。对于 Neo4j 来说,虽然它的性能读写性能⾮常优越和原⽣存储,但是因为 3.x 版本之后,社区版已经不再⽀持分布式模式,所以说肯定不能达到我们预期的要求。JanusGraph 和 NebulaGraph 均支持分布式扩展和存算分离架构,但前者的存储、索引均依赖于第三方组件,带来大量额外运维工作,其支持分布式事务,而 NebulaGraph 不支持分布式事务处理。资产平台设计的匹配性因素,核心因素是数据隔离、属性及 Schema 数量上线、属性类型、查询语言等。JanusGraph/Neo4j 社区版属性集均不支持强 Schema,这意味着更灵活的属性配置。同时,属性类型也支持诸如 map、set 等复杂类型。NebulaGraph 属性集虽然有强 Schema 依赖,但属性和 Schema 数量没有上限,也支持 Schema 的修改,唯一美中不足的是不支持 map/set 等复杂类型属性,这将对类型定义和系统设计有一定的影响,以及对潜在的需求场景有一定的约束。三种数据库均有通用的查询语言、以及可以基于 GraphX 进行图算法分析。为什么选择 NebulaGraph基于以下四点的考虑,众安选择了 NebulaGraph——第⼀是分布式的存算分离架构,可以以最优的成本,快速扩缩容相关服务。第二是外部组件依赖较少,⽅便运维。第三是卓越的读写性能,在19 年年底众安金融风控场景,我们对 NebulaGraph 就进⾏了⼀定的性能测试,我们在纯 nGQL的 insert 这种写入方案下,通过 DataX 可以实现 300w record/s 的数据写⼊速度,这个是一个非常惊人的数据同步的体验。第四是数据存储格式,因为众安有大量的子公司租户,需要进行数据的存储隔离,如果是其他产品就需要部署多套图库,或一套图库数据里打租户标签。NebulaGraph可以使用图空间的方式实现天然的数据隔离,大大简化了我们开发的工作量。NebulaGraph 阿⾥云部署模式因为众安没有独立机房,所有的服务均依赖于阿里云金融云,基于阿⾥云 ECS 的能力,可以快速实现服务器以及服务器上存储资源的弹性扩收容。实际部署中,我们将 graphd 跟 mated、 storaged 进行了分开部署,避免大量查询导致内存过高,影响到其他图数据服务的稳定性。graphd 占用了 2 台 4C 8G 服务器,metad/storaged 占用了 3 台 4C 16G 服务器。当前资产平台的实体数量在 2,500w 个左右,边数据在 4左右,主要为数据集类型数据。我们使用每台 ECS 使用了两块 200G 的 ESSD 进行存储,根据 NebulaGraph 的推荐,磁盘的数量越多,图空间 Partition 的扩展的数量就可以越多,可以获得更好的并发处理能力。众安在NebulaGraph中的模型设计下面介绍一下基于 NebulaGraph 的模型设计。对于实体定义来说,对应 NebulaGraph 的某一个 Tag,其相对于其他图数据库类似于 Label 概念,就是某个属性集的名称,通过 Tag 可以更快检索倒到某一个实体点下的属性,类型定义的 Tag 必须同这一类型的点 ID 进行强绑定,注册时需要进行相关校验。另一个属性集的概念是公共标签,公共标签可以做很多事情,比如业务属性集、实体标签等。公共标签在 NebulaGraph 当中也对应一个 Tag,这个 Tag 可以绑定到多种不同的实体,比如环境公共标签,可以赋给 MySQL 数据源实体,也可以赋给 MaxCompute数据源实体等。对于关系定义来说,对应 NebulaGraph 中的某个 Edge Type,类型定义中的来源目标端点的实体类型,必须同这一类型的点 ID 进行强绑定,注册时需要进行相关校验。对于数据隔离来说,上述实体和关系模型,通过 NebulaGraph 的图空间进行隔离,在众安内部的多个租户实体下,比如保险、小贷、科技等,会在租户初始化时创建指定图空间,后续的类型定义均在租户图空间下进行。最后我们再来看⼀下模型设计的继承关系。我们所有的实体根节点是⼀个叫做 Asset 的实体定义,我们将一些公共属性定义其中,包括名称、展示名称、备注、类型等;基于 Asset 类型,我们实现了对接平台的各种资产实体,报表平台里的模型、视图、画布、⻔户等实体,数据超市里的路由 API、数据 API 以及外部扩展 API 等实体,开发工台里的调度任务、流计算任务、工作空间、文件等实体,以及两个比较特殊的资产属主实体和服务资产实体。另一个特殊的实体是数据集实体,我们将不同数据源数据源、表、列等信息均定义了独立的资产实体定义,以便实现不同数据源的差异化属性展示。我们最终的全链路数据资产,均是通过数据集及其子类自定义实现串联,从而实现跨平台的链路分析。比如调度作业的库表血缘,可以关联到报表平台的数据模型,也可以关联到数仓的 Data API 依赖的 Table Store 的某张表等等。03 未来展望2022年年底,众安基本上已经实现了各个平台的各种资产的注册跟上报的过程。2023年,我们将在围绕数据资产割接、数据安全管理和数据治理三个方面进行扩展性开发。数据资产割接,将站在用户实体的角度上,快速识别个人关联的数据资产,时间属主资产切换和离职交接功能。数据安全管理,基于资产平台的能力做出多种扩展,迁移内部老元数据系统的表分级、权限审批功能;内部脱敏规则配置平台及 SDK,扩展支持基于表分级数据加密和白名单策略等。数据治理,基于资产平台的全链路血缘分析能力,观察资产热度、使用等关键指标,清理无效作业和重复计算作业,实现降本增效,减少云平台使用费用。要来感受同众安科技一样的图数据库体验嘛?NebulaGraph 阿里云计算巢现 30 天免费使用中,点击链接来搭建自己的资产管理系统吧!
文章
存储  ·  消息中间件  ·  NoSQL  ·  关系型数据库  ·  API  ·  atlas  ·  调度  ·  数据安全/隐私保护  ·  索引  ·  容器
2023-03-27
常用Docker软件安装
jdkdockere pull openjdk:11docker run -d -t --name java-11 openjdk:11 MySQL可以从docker hup中查找自己想要安装的版本 docker pull mysql:5.7 拉取镜像 创建容器 # 在/root目录下创建mysql目录用于存储mysql数据信息 mkdir /root/mysql cd /root/mysql docker run -id \ -p 3307:3306 \ --name=ydl_mysql \ -v /root/mysql/conf:/etc/mysql/conf.d \ -v /root/mysql/logs:/logs \ -v /root/mysql/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ mysql:5.7 进入容器 docker exec -it ydl_mysql /bin/bash 进入MySQL mysql -uroot -p 123456 远程连接MySQL exit退出 如果远程连接有问题 https://www.cnblogs.com/zhangxiaoxia/p/13043508.html 修改MySQL的字符编码 修改my.cnf 文件 cd /mydata/mysql/conf vim my.conf [client] default-character-set=utf8mb4 [mysql] default-character-set=utf8mb4 [mysqld] init_connect='SET collation_connection = utf8mb4_bin' init_connect='SET NAMES utf8mb4' character_set_server = utf8mb4 collation_server = utf8mb4_bin skip-character-set-client-handshake skip-name-resolve -p 3307:3306:将容器的 3306 端口映射到宿主机的 3307 端口。-v $PWD/conf:/etc/mysql/conf.d:将主机当前目录下的 conf/my.cnf 挂载到容器的 /etc/mysql/my.cnf。配置目录-v $PWD/logs:/logs:将主机当前目录下的 logs 目录挂载到容器的 /logs。日志目录-v $PWD/data:/var/lib/mysql :将主机当前目录下的data目录挂载到容器的 /var/lib/mysql 。数据目录-e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码。MySQL5.7谷粒商城 1 docker pull mysql:5.7 下载docker镜像2 运行MySQL 容器docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 参数说明 -p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口 -v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂载到主机 -v /mydata/mysql/log:/var/log/mysql:将日志文件夹挂载到主机 -v /mydata/mysql/data:/var/lib/mysql/:将配置文件夹挂载到主机 -e MYSQL_ROOT_PASSWORD=root:初始化 root 用户的密码为root3 SQLyong进行远程连接grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option; flush privileges 刷新权限 4 修改配置文件cd /mydata/mysql/conf vim my.conf [client] default-character-set=utf8mb4 [mysql] default-character-set=utf8mb4 [mysqld] init_connect='SET collation_connection = utf8mb4_bin' init_connect='SET NAMES utf8mb4' character_set_server = utf8mb4 collation_server = utf8mb4_bin skip-character-set-client-handshake skip-name-resolve 这个字符编码不要设置为utf8 MySQL容器会启动不起来 报错 Error response from daemon: Container 7819b1b3c5a7f3efe4ec7e8bab59e80ad13f10a57b7b5484f664b205d3c1ce0d is not running 这个是错误示范 [client] default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake skip-name-resolve [mysqld] skip-name-resolve Tomcat官方的webapps是没有文件是需要自己弄得docker search tomcat docker pull tomcat 创建端口映射 # 在/root目录下创建tomcat目录用于存储tomcat数据信息 mkdir /root/tomcat cd /root/tomcat docker run -id --name=ydl_tomcat \ -p 8081:8080 \ -v /root/tomcat:/usr/local/tomcat/webapps \ tomcat 外部访问 http://宿主机ip:8081/Nginxdocker search nginx docker pull nginx 端口映射 # 在/root目录下创建nginx目录用于存储nginx数据信息 mkdir /root/nginx cd /root/nginx mkdir conf cd conf 文件配置 # 在~/nginx/conf/下创建nginx.conf文件,粘贴下面内容 vim nginx.conf user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } docker run -id --name=ydl_nginx \ -p 80:80 \ -v /root/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ -v /root/nginx/logs:/var/log/nginx \ -v /root/nginx/html:/usr/share/nginx/html \ nginx 外部机器访问Redisdocker search redis docker pull redis:5.0 docker run -id --name=ydl_redis -p 6380:6379 redis:5.0 外部连接谷粒商城1下载redis docker pull redis 如果不先创建这个conf文件 等会目录进行挂载的时候会吧redis.conf文件当成一个目录 mkdir -p /mydata/redis/conf && touch /mydata/redis/conf/redis.conf 2 启动容器 目录挂载 docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \ -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis redis-server /etc/redis/redis.conf 启动redis客户端 3 docker exec -it redis redis-cli 4 在redis.conf 中写入 appendonly yes 开启AOF 持久化需要配置密码 如果不陪着密码很危险1在创建容器的时候配置密码 --requirepass 2 创建容器之后配置密码 docker exec -it 容器ID bash 进入redis目录 cd /usr/local/bin 运行命令: redis-cli 设置redis密码 config set requirepass 密码 如出现:(error) NOAUTH Authentication required 这是因为redis设置了密码,我们需要使用密码来进行验证之后再来对redis客户端进行操作,否则我们没有操作redis缓存数据库的权限。 需要用 auth 密码如果使用redis 连接 Another Redis Desktop Manager报错 Redis Client On Error: ReplyError: WRONGPASS invalid username-password pair or user is disabled. Con 就不要设置用户名部署ELKelasticsearch安装1 下载镜像docker pull elasticsearch:7.4.22 创建和容器内配置文件映射的文件mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data3 添加配置文件echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml4 添加权限chmod -R 777 /mydata/elasticsearch/5 运行容器 docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \ -e "discovery.type=single-node" \ -e ES_JAVA_OPTS="-Xms256m -Xmx512m" \ -v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2kibandocker pull kibana:7.4.2docker run --name kibana \ -e ELASTICSEARCH_HOSTS=http://120.78.150.188:9200 -p 5601:5601 \ -d kibana:7.4.2但是还要进行目录挂载修改 yml文件 设置中文IK分词器Ik分词器版本要和ES和Kibana版本保持一致 不然可能启动不了docker 进入容器此命令需要在容器中运行elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.0/elasticsearch-analysis-ik-7.8.0.zip退出容器,重启容器exit docker restart es7.8nacos如果用docker安装naocs如果是 2版本可能会启动不了 docker pull nacos/nacos-server:1.3.1docker run \ --name nacos -d \ -p 8848:8848 \ --privileged=true \ --restart=always \ -e JVM_XMS=256m \ -e JVM_XMX=256m \ -e MODE=standalone \ -e PREFER_HOST_MODE=hostname \ nacos/nacos-server:1.3.1访问测试 http://43.138.137.168:8848/nacosMQdocker run \ -e RABBITMQ_DEFAULT_USER=zhuxiaoyi \ -e RABBITMQ_DEFAULT_PASS=412826zxyZXY \ --name rabbitmq \ --hostname mq1 \ -p 15672:15672 \ -p 5672:5672 \ -d \ rabbitmq:3-managementNginxmkdir /root/docker/nginx mkdir /root/docker/nginx/conf由于我们现在没有配置文件,也不知道配置什么。可以先启动一个nginx,讲他的配置文件拷贝出来再作为映射,启动真正的nginxdocker pull nginx:1.17.4 docker run --name some-nginx -d nginx:1.17.4 docker container cp some-nginx:/etc/nginx /root/docker/nginx/conf然后就可以删除这个容器了docker docker rm -f some-nginx在重新启动nginxdocker run --name nginx -p 80:80 \ -v /root/docker/nginx/conf:/etc/nginx \ -v /root/docker/nginx/html:/usr/share/nginx/html \ -d nginx:1.17.4FTP服务器1 需要账号和密码docker run -v /data/dav:/usr/local/nginx/html -d -p 88:80 lutixiaya/nwebdav:latest chmod o+w /data/davip+端口访问测试 需要输入账号和密码使用winscp 进行连接1、点击新建站点 2、选择协议 3、输入服务器ip 4、输入端口 5、输入用户名,默认用户:admin 6、输入密码,默认密码:bash.lutixia.cn 7、登录https://zhuanlan.zhihu.com/p/573721115 参考链接2 无需账号和密码在同一个文件目录下准备好这个三个文件start-nginx.sh#!/bin/bash mkdir data docker stop nginx_file_server docker rm nginx_file_server docker run -d -p 8081:8080\ --name nginx_file_server \ -v $(pwd)/data:/data \ -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \ -v $(pwd)/default.conf:/etc/nginx/conf.d/default.conf \ nginx:stable-alpine nginx.confnginx.confuser root; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } default.confdefault.confserver { listen 8080; #端口 server_name localhost; #服务名 # for SSL listen port only #ssl_certificate /etc/nginx/conf.d/server.pem; #ssl_certificate_key /etc/nginx/conf.d/server-key.pem; #ssl_protocols TLSv1.2; #ssl_prefer_server_ciphers on; #ssl_session_timeout 5m; #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #underscores_in_headers on; charset utf-8; # 避免中文乱码 root /data; #显示的根索引目录,注意这里要改成你自己的,目录要存在 location / { autoindex on; #开启索引功能 autoindex_exact_size off; # 关闭计算文件确切大小(单位bytes),只显示大概大小(单位kb、mb、gb) autoindex_localtime on; # 显示本机时间而非 GMT 时间 } }chmod +x start-nginx.sh && ./start-nginx.sh`测试一下:echo file_server > data/file1.txt打开浏览器 http://127.0.0.1:8081/DockerComposesudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-compose docker-compose --version安装GitLab1 下载镜像docker pull twang2218/gitlab-ce-zh2 启动容器docker run -d -p 8443:443 -p 8090:80 -p 8022:22 --restart always --name gitlab -v /usr/local/gitlab/etc:/etc/gitlab -v /usr/local/gitlab/log:/var/log/gitlab -v /usr/local/gitlab/data:/var/opt/gitlab --privileged=true twang2218/gitlab-ce-zh3 进入容器修改配置文件由于进行了目录映射 也可以不在容器内部进行修改docker exec -it gitlab bash cd /etc/gitlab vim /etc/gitlab/gitlab.yml4 修改配置文件搜索URL external_url 'http://gitlab.example.com'把url换成自己的external_url 'http://116.205.133.97/' nginx['listen_port'] = nilnginx['listen_port'] = 82 这个是注释掉的 5 重启服务这是在容器内部重启服务gitlab-ctl restartgitlab是有很多组件组成的只有这些组件都运行成功了,才启动成功。6 访问测试http://120.78.214.226:8090第一次登录默认是root用户 密码自己设定 不要低于8位安装Jenkins1 下载镜像docker pull jenkinsci/blueocean 中文版本2 创建目录# mkdir /home/jenkins_home # chown -R 1000:1000 /home/jenkins_home/ # chown -R 1000:1000 /usr/local/src/jdk/jdk1.8/ # chown -R 1000:1000 /opt/apache-maven-3.5.0/3 运行容器docker run \ -d \ --name jenkins \ -p 9999:8080 \ -p 8888:8888 \ -p 50000:50000 \ -v /usr/local/src/jdk/jdk1.8:/usr/local/src/jdk/jdk1.8 \ -v /opt/apache-maven-3.5.0:/opt/apache-maven-3.5.0 \ -v/home/jenkins_home:/var/jenkins_home \ jenkins/jenkins:2.222.3-centos4 查看密码docker exec -it jenkins bash cat /var/jenkins_home/secrets/initialAdminPassword b0468f2eb870422abf509fe59f74e003 5 访问测试http://120.78.214.226:9999/6 进行汉化 在安装插件页面输入 chinese 7 替换插件下载地址https://blog.csdn.net/weixin_45878889/article/details/123867587 安装SonarQubehttps://blog.csdn.net/OfficerGoodbody/article/details/126662724新版SonarQube不支持MySQL1 下载postgres镜像docker pull postgres2 创建文件mkdir -p /opt/postgres/postgresql mkdir -p /opt/postgres/data3 创建网络docker network create sonarqube4 运行postgres 容器docker run --name postgres -d -p 5432:5432 --net sonarqube \ -v /opt/postgres/postgresql:/var/lib/postgresql \ -v /opt/postgres/data:/var/lib/postgresql/data \ -v /etc/localtime:/etc/localtime:ro \ -e POSTGRES_USER=sonar \ -e POSTGRES_PASSWORD=sonar \ -e POSTGRES_DB=sonar \ -e TZ=Asia/Shanghai \ --restart always \ --privileged=true \ --network-alias postgres \ postgres:latest5 安装 sonarQubedocker pull sonarqube6 准备文件夹mkdir -p /opt/sonarqubeecho "vm.max_map_count=262144" > /etc/sysctl.conf sysctl -p7 先运行一下拷贝文件docker run -d --name sonarqube sonarqubedocker cp sonarqube:/opt/sonarqube/conf /opt/sonarqube docker cp sonarqube:/opt/sonarqube/data /opt/sonarqube docker cp sonarqube:/opt/sonarqube/logs /opt/sonarqube docker cp sonarqube:/opt/sonarqube/extensions /opt/sonarqube8 删除容器docker stop sonarqube docker rm sonarqube9 添加权限chmod -R 777 /opt/sonarqube/10 修改配置文件vim /opt/sonarqube/conf/ sonar.properties 修改账号和密码 sonar.jdbc.username=sonar sonar.jdbc.password=sonar sonar.jdbc.url=jdbc:postgresql://postgres:5432/sonar11 运行容器docker run -d --name sonarqube -p 9090:9000 \ -e ALLOW_EMPTY_PASSWORD=yes \ -e SONARQUBE_DATABASE_USER=sonar \ -e SONARQUBE_DATABASE_NAME=sonar \ -e SONARQUBE_DATABASE_PASSWORD=sonar \ -e SONARQUBE_JDBC_URL="jdbc:postgresql://postgres:5432/sonar" \ --net sonarqube \ --privileged=true \ --restart always \ -v /opt/sonarqube/logs:/opt/sonarqube/logs \ -v /opt/sonarqube/conf:/opt/sonarqube/conf \ -v /opt/sonarqube/data:/opt/sonarqube/data \ -v /opt/sonarqube/extensions:/opt/sonarqube/extensions\ sonarqube12测试访问浏览器输入http://ip:9090,开始初始化数据库初始化成功后进入登录界面,账号:admin 密码:admin轻量级容器监控portanier下载 docker pull lihaixin/portainerdocker run -d -p 9000:9000 --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ --name portainer lihaixin/portainer访问测试 http://43.138.137.168:9000/#/home重量级 容器监控docker-compose.yml配置version: '3.1' volumes: grafana_data: {} services: influxdb: image: tutum/influxdb:0.9 restart: always environment: - PRE_CREATE_DB=cadvisor ports: - "8083:8083" - "8086:8086" volumes: - ./data/influxdb:/data cadvisor: image: google/cadvisor links: - influxdb:influxsrv command: -storage_driver=influxdb - storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086 restart: always ports: - "8080:8080" volumes: - /:/rootfs:ro - /var/run:/var/run:rw - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro grafana: user: "104" image: grafana/grafana user: "104" restart: always links: - influxdb:influxsrv ports: - "3000:3000" volumes: - grafana_data:/var/lib/grafana environment: - HTTP_USER=admin - HTTP_PASS=admin - INFLUXDB_HOST=influxsrv - INFLUXDB_PORT=8086 - INFLUXDB_NAME=cadvisor - INFLUXDB_USER=root - INFLUXDB_PASS=root docker-compose up -d
文章
NoSQL  ·  关系型数据库  ·  MySQL  ·  应用服务中间件  ·  Redis  ·  Nacos  ·  数据安全/隐私保护  ·  nginx  ·  Docker  ·  容器
2023-03-27
如何轻松应对偶发异常
作者:亦盏在之前的文章中,我们已经介绍了如何通过 MSE 提供的无损上下线和全链路灰度这两个功能来消除变更态的风险,相信您已经能够在变更时得心应手。但是在应用运行过程中突然遇到流量洪峰、黑产刷单、外部依赖故障、慢 SQL 等偶发异常时,您如何能够继续轻松应对呢?本文将通过介绍 MSE 微服务治理在三月的最新功能来告诉您答案。文章主要分为三部分,首先是 MSE 微服务治理的简介,然后是 MSE 全面消除变更态风险的回顾 ,最后是如何借助 MSE 微服务治理轻松应对线上偶发异常。MSE 微服务治理简介首先看一下微服架构的总览,相信读者朋友们对微服务都不陌生。在 JAVA 中比较主流的微服务框架就是 Dubbo 和 Spring Cloud,同时现在也有很多公司采用 Go、Rust 这些语言去构建自己的微服务。下图中浅绿色的这一部分都是纯开源的内容,除了微服务框架本身外,还包含了服务注册发现、服务配置中心、负载均衡等一系列的组件。大家也发现,在微服务实施的过程中,随着微服务的增多、调用链路的复杂化,出问题和故障的可能性都在增加,问题定位的难度也成倍上升。比如说在可观测领域,需要使用到应用监控、链路追踪、日志管理等功能,才能放心地将应用部署到线上,以免出现问题无法定位。更进一步,微服务本身更需要引入微服务治理。在开发态,需要通过服务 mock、服务测试、服务元数据等治理功能去保证微服务的快速迭代;在变更态,需要借助无损上下线和全链路灰度功能去保证发布时的稳定性,以及业务的连续性;在运行态,微服务可能遇到各式各样的偶发异常,需要借助治理功能去实现流量的自愈,保证我们的业务稳定、流畅地运行。很难想象一个线上的微服务应用没有治理是个什么场景,可以说是无治理、不生产。随着使用微服务架构的公司越来越多,大家也都意识到微服务治理非常重要,但是在实施微服务治理的过程中,大家还是遇到了比较多的痛点和难点。我们总结了一下,企业在实施微服务的过程中遇到的三类问题,分别是稳定、成本和安全。首先是稳定的问题微服务在发布的过程中特别容易出现问题,每次发布都得熬夜以避开业务高峰期,而且还是免不了出现业务中断。当遇到难得的业务爆发式增长时,应用因为无法承载流量高峰导致整体宕机,错失了业务爆发的机会。在出现一些偶发异常的时候,应用没有完善的防护和治愈手段,可能会因为偶发的小异常,不断的滚雪球,最终去拖垮我们整个集群和整个服务。遇到偶发问题的无法准确定位和彻底解决,只能重启方式规避,从而使得我们微服务的隐患越积越多,风险越来越大。第二是成本的问题微服务治理功能在开源没有完善的解决方案,这个时候可能是需要去招聘大量精通微服务的人才,才能自建一套微服务治理体系,但是也无法覆盖完整的治理场景。这会使得人力成本非常高。引入了微服务之后,基本都会有一些敏捷开发、快速迭代的诉求。在这个过程中,需要维护多套环境以维持一个并行开发迭代的需求,这会使得机器成本非常高。传统的开发模式,当需要引入一个治理功能升级的时候,需要先升级基础组件,再对所有的应用进行代码改造,依次发布才能完成升级,改造的周期长、风险大。这会使得时间成本非常高。最后是安全的问题经常会有一些开源的框架的安全漏洞被暴露出来,这个时候需要对依赖进行升级以修复 Bug,走一次完整的发布才能实现,成本高,时效性差。零信任安全的解决方案正在被越来越多的公司采用,去保障整体的业务安全。不仅是在流量入口层对业务进行安全的保障,在应用间的内部调用也需要有完整的鉴权和保障机制。那么 MSE微服务治理是怎么去解决这三个问题的?MSE 的微服务治理基于开源的 OpenSergo 标准实现了无侵入的微服务治理。对于使用者来说升级成本是零,业务是完全无侵入感知的。目前已经可以做到是五分钟接入,半小时内就学会完整的使用,保障微服务应用在变更态和运行时的稳定性。因为 OpenSergo 是全面开源标准,不会有任何的厂商绑定的问题。从上图中可以看出,微服务应用可以通过 Java Agent 或 Sidecar 的方式接入MSE 微服务治理。在接入治理后,可以通过 MSE 微服引擎的控制面配置治理规则的配置。应用收到规则之后会实时生效,及时地保护应用。全面消除变更态风险的回顾现在来回顾一下,变更态存在哪些问题,以及 MSE 是如何全面消除变更态风险的。首先问大家一个问题,当你的代码没有问题的时候,是不是在发布的过程中就一定不会影响到业务?答案其实是否定的,因为这个时候可能会遇到无损上下线的问题。为什么会出现无损下线问题?第一个原因是服务消费者无法实时感知到服务提供者已经下线了,仍旧会去调用一个已经下线的地址,从而出现一个影响业务的报错。同时服务提供者也可能会在请求处理到一半的时候就直接停止了,从而导致业务报错,甚至出现数据不一致的问题。为什么会出现无损上线的问题?一个新启动的节点,有可能在还没完全启动前就注册到注册中心了,进而导致这时候过来的流量无法被正确处理导致报错;另一种情况是,虽然应用启动完成了,但是还没有处理大流量的能力,可能直接被大流量压垮,需要先进行预热,才能处理大流量的请求。除此之外,目前 K8s 的普及率已经非常高了,如果微服务的生命周期没有与 K8s 生命周期做一个的 readiness 对齐的话,发布过程也容易出现无损上线问题。当然更普遍的情况是,谁都不敢拍着胸脯保证我的代码没有问题。如果没有稳定的灰度机制,对只有两个节点的应用来说,发布一台就会影响 50% 的业务。发现 bug 之后需要回顾,可怕的是回滚的速度还非常慢,甚至回滚的时候还出现了更大的问题。那么 MSE 是如何去解决这两个问题的。首先我们看一下这个无损下线的这个问题。对于一个已经接入了MSE 微服务治理的应用,无损下线功能是默认开启的,我们可以在 MSE 的控制台中看到无损下线的完整的流程。首先在无损下线开始时,sc-B 这个服务的提供者 10.0.0.242 会提前向注册中心发起注销动作。注销之后,10.0.0.242 会去主动去通知它的服务消费者当前节点已下线。上图中的例子,服务 sc-B 的消费者 10.0.0.248 和 10.0.0.220 这两个消费者收到了下线通知。10.0.0.248 和 10.0.0.220 在收到下线通知后,都会把 10.0.0.242 的地址维护在 offlineServerIp 列表中,并且找到 sc-B 对应的调用列表,在调用列表中移除 10.0.0.242 这个 IP同时,10.0.0.242 在停止的过程中会做一个自适应的等待,确保所有在途请求都处理完毕才停止应用。我们再看一下这个无损上线的这个问题。对于一个已经接入了 MSE 微服务治理的应用,可以通过控制台开启无损上线功能,开启后,我们可以在 MSE 的控制台中看到无损上线的完整的流程。从上图中我们可以看到,应用配置的延迟注册时间是 10S,预热时间是 120S,预热曲线是拟合二次曲线。根据 QPS 数据可以看到,应用在注册完成之后才有流量进入,同时在预热开始后 120S 内,流量是一个缓慢上升的过程,直到预热结束之后,流量才开始进入平稳的状态。看完了在代码没问题的情况下如何保证变更态的稳定性,我们在看看当代码可能存在问题的时候,如何保障变更态的稳定性。拿一个简单的架构作为例子,微服务的整体调用链路是网关调用 A,A 调用 B ,B 再调用 C 这么一个链路。某次迭代中,有个特性的修改需要同时修改 A 和 C 这两个应用。在发布的过程中,需要通过全链路灰度发布来验证 A 的新版本和 C 的新版本的正确性。假设 x-user-id 为 120 的用户是我们一个不那么重要的用户。那么我们可以配置只有 x-user-id 为 120 的这一个用户,他才会访问新版本。在具体的调用中,网关会先访问 A 的灰度版本,A 在调用 B 的时候发现 B 没有一个灰度的版本,所以只是 fall back 到 B 的基线版本。但是 B 在调用 C 的时候,还是记住了 x-user-id 为 120 的流量属于一个灰度流量。同时又发现 C 存在灰度节点,流量还是会重新回到 C 的灰度节点。如上图所示,可以通过 MSE 全链路灰度控制新版本的影响面。将参与到全链路灰度的应用添加到泳道组后,配置如上图所示的规则,只有 x-user-id 为 120 的这个用户他才会被路由到新版本。也就是说即使这个新版本问题再大,那只是这一个用户会受到影响。同时做一个回滚的动作也是非常方便的,只需要把灰度规则关闭或者删除即可,就不会有流量被路由到新版版本。经过灰度的谨慎验证后,可以视情况继续扩大灰度的影响面,或者直接全量发布,从而实现安全的版本变更。刚才我们也提到过,全链路灰度其实是一个非常复杂的过程,除了 RPC 的灰度外,还包含了前端灰度、消息灰度、异步任务灰度、数据库灰度等场景,这些场景 MSE 都做了一些探索和支持。以消息灰度为例, MSE 已经完整地支持了 RocketMQ 的灰度,实现了全链路灰度的闭环,是一个久经生产考验的全链路灰度。以上是 MSE 全面消除变更态风险的回顾。如何轻松应对偶发异常在回顾了一下 MSE 是如何做到全面消除变更态风险之后。我们来看一下 MSE 微服务治理在三月份新上线的这些功能,如何帮助大家轻松应对偶发异常。我们做了大致的总结和归类,将偶发异常的情况分为了两部分:异常流量和不稳定服务依赖。接下来我们将面向这两个大类下五个小类的场景,来阐述如何借助 MSE 微服务治理三月份发布的新功能去解决这些偶发异常的。接口限流首先我们看一下激增流量这一块。拿一个具体的场景来说,业务方精心准备了一个大促活动,活动非常火爆,在活动开始的一瞬间,远远超过系统承载预期的用户进来抢购秒杀,如果没有微服务治理能力,服务可能由于扛不住流量直接被打挂,甚至在重启的过程还不断被打挂。精心准备的活动由于系统宕机导致效果非常差,在遇到难得的业务爆发式增长机会时,却因为应用的宕机错失了爆发的机会。在这个场景下,可以借助 MSE 的流控能力对关键接口配置流控规则保护应用整体的可用性。只让容量范围内的请求被处理,超出容量范围外的请求被拒绝,相当于这是一个安全气囊的作用。如上图所示, /a 这个接口是我们应用的关键接口,我们识别到单机阈值是 200 后,配置上流控防护规则,使得这个关键接口最大的通过 QPS 是 200,超出阈值的流量会直接被拒绝,从而保护应用整体的可用性。精准限流接下来我们再看一下精准限流,拿一个具体的场景来说,由于在配置优惠活动的时候没有配置好最大次数,或者存在规则上的漏洞。在被黑产发现后不断地进行在刷单,业务损失严重。在这个场景下,我们可以借助 MSE 的精准限流能力实现防黑产刷单。MSE 的精准限流可以在 API 的维度基础上,基于流量的特征值来进行限流,比如根据调用端的 IP,参数中的某一个参数的值,或者说 HTTP 请求里 header 的特征等。如上图所示,当我们识别到黑产用户的特征之后,可以根据 header 里面 key 为 user-id 的值来进行精准的业务限流,这里配置的规则为每天只能通过一次,也就意味着黑产流量被识别到之后,一天内超出一次的调用都会被拒绝,这样就能有效地防止黑产刷单保护业务。并发隔离第三部分是并发隔离,拿一个具体的场景来说,假设我们的应用依赖了一个第三方的支付渠道,但是因为渠道本身的原因出现了大量的慢调用,进而导致了应用全部的线程池都被调用该支付渠道的调用占满了,没有线程去处理其他正常的业务流程。而且这个第三方的支付其实只是我们其中的一个支付渠道。我们其他的支付渠道都是正常的。MSE 可以对应用作为客户端的流量进行并发隔离,限制同时调用这个第三方支付渠道的最大并发数,从而留下充足的资源来处理正常的业务流量。如上图所示,我们配置了并发数阈值是 5,每个应用节点最多同时存在 5个并发去调用第三方支付服务,当调用数超过了 5 的时候,请求在发起前就会直接被拒绝。通过并发隔离,把不稳定的支付渠道隔离到有限的影响面,而不会挤占其他正常支付渠道的资源,这样来保障我们一个业务的稳定性。SQL 防护第四部分是 SQL 防护,拿一个具体的场景来说,应用更新后出现了慢 SQL 的语句,处理的时间比较长的,这个时候需要快速定位慢 SQL ,然后防止我们这个应用去被这个慢 SQL 拖垮了。或者在项目的初期可能没有对这个 SQL 的性能做一个很好的考量,然后随着业务的发展,业务量级的增加,然后导致线上遗留老 SQL 逐渐腐化,成为了一个慢 SQL。MSE 的数据库治理功能,可以自动统计应用的 SQL语句,以及 SQL 的 QPS、平均耗时 和最大并发等数据,并针对于 SQL 进行一个防护。如上图所示,在平均耗时中找到慢 SQL,点击右边的 SQL 防护按钮,通过配置并发隔离规则来进行保护应用不被慢 SQL 拖垮。服务熔断最后我们再来看一下服务熔断。拿一个具体的场景来说,在业务高峰期查询积分服务达到性能瓶颈,导致响应速度慢、报错增多。但是它并不是一个关键的服务,这时不能因为无法查询积分余额而导致整个流程都无法进行。正确的逻辑应该是积分余额暂时不显示,但是除此之外其他主流程都能正常完成。MSE 的服务熔断功能,可以很好地解决上述的问题。MSE 支持通过慢调用的比例,或者错误数去衡量一个服务是否处于正常状态,当识别到服务已经不正常的时候就自动触发熔断。熔断后消费者不会再去调用出问题的服务了,而是直接返回 Mock 值。如上图所示,配置当异常比例超过 80% 的时候自动触发熔断,不再去调用不正常的服务。一方面消费者不再去调用不正常的弱依赖服务,不会因为弱依赖的问题导致主流程不正常。另一方面,熔断也给了不稳定的下游一些喘息的时间,让它有机会去恢复。我们最后再来总结一下 MSE 微服治理是如何助您轻松应对线上偶发异常的,针对我们刚才总结的这五类场景:当遇到激增流量的时候,我们应该使用接口限流。当我们遇到黑产刷单等问题的时候,我们应该使用精准限流。当第三方服务不响应,占满线程池的时候,我们应该使用并发隔离。当应用存在慢 SQL 的时候,我们应该使用 SQL 防护。当应用的弱依赖出现异常,影响我们核心业务流程的时候,我们应该使用服务熔断。通过这五个功能,MSE 微服务治理可以助您去轻松去应对上述场景的一些偶发异常,后续 MSE 也会持续不断地去探索和拓展更多场景,更全面地保障您应用的运行时的稳定性。结语MSE 微服务治理致力于帮用户低成本构建安全、稳定的微服务。由于篇幅原因,更多的功能就不在这里给大家详细介绍了,关于 MSE 更多的详情,读者可以通过查看 MSE 帮助文档的了解详情。MSE 帮助文档:https://help.aliyun.com/document_detail/126761.html欢迎感兴趣的同学扫描下方二维码加入钉钉交流群,一起参与探讨交流。MSE 3 月采购季优惠【0元试用】服务治理试用版,开通后 30 天免费使用。【新老同享】服务治理资源包:专业版/(7200Agent*小时)67.5 元。注册配置中心资源包:开发版/规格 1c2g 76.7 元【首购专享】注册配置中心专业版,引擎类型 Nacos,包年包月 6 折。注册配置中心专业版,引擎类型 ZooKeeper,包年包月 5 折。云原生网关,包年包月 6 折。服务治理资源包(按量抵扣),包年包月 6 折。
文章
SQL  ·  Kubernetes  ·  安全  ·  Cloud Native  ·  Java  ·  测试技术  ·  数据库  ·  黑灰产治理  ·  微服务  ·  容器
2023-03-27
C++学习路线
C++是一种高级编程语言,广泛用于开发操作系统、应用程序、游戏和各种工具。如果你想学习这门语言,以下是一个适合初学者的学习路线:第一步:学习C++基础知识在学习C++之前,你需要掌握一些基础知识,如计算机科学和编程方面的基础概念。你可以通过阅读相关书籍、观看视频教程或参加在线课程来学习这些知识。这些资源应该涵盖以下主题:数据类型、变量和表达式控制语句(如if语句和循环)函数和参数数组和字符串指针和引用除此之外,初学者还可以学习一些其他的编程语言,比如Python。学习Python可以帮助你更好地理解编程的基本概念和原理。第二步:学习C++标准库一旦你掌握了基础知识,你就可以开始学习C++标准库了。C++标准库是C++编程的核心,包含各种有用的函数和类型,可用于开发各种类型的应用程序。你需要学习以下内容:输入和输出(如cin和cout)字符串和字符处理函数容器(如vector和map)算法(如排序和搜索)此外,你还可以学习一些其他的库和框架,如Boost和STL等。这些库和框架可以帮助你更好地理解C++编程和提高编程效率。第三步:学习C++高级主题一旦你掌握了基础知识和标准库,你就可以开始学习C++的高级主题了。这些主题可能包括以下内容:面向对象编程模板和泛型编程异常处理多线程编程除此之外,你还可以学习一些其他的高级主题,如网络编程、图形界面编程等。这些主题可以帮助你更好地应用C++编程。第四步:练习和实践学习C++需要大量的实践和练习。你可以完成一些课程作业或参与开源项目,或者自己设计和实现一些小型应用程序。这样可以帮助你巩固所学知识并提高编程技能。除此之外,你还可以参加一些编程比赛或者挑战赛,这样可以帮助你更好地应用所学知识。总结以上是一个适合初学者的C++学习路线,当然还有很多其他的资源和方法可以帮助你学习C++。最重要的是,持续不断地学习和实践,才能成为一名优秀的C++编程人员。不断地阅读相关的书籍和文章,与其他编程人员交流,也可以帮助你更好地学习和成长。
文章
算法  ·  C++  ·  Python  ·  容器
2023-03-27
数字化转型下一程,Sitecore推动数实融合快速实现
毫无疑问,数字化转型在2023年依然是品牌的核心重点,但只要仔细观察就会发现这一主题已经悄然的发生某些变化,与线上蓬勃发展相对的是,线下经济已经不可抑制显露出疲态,为解决这样的现状,推动两者之间的深度综合,提升综合竞争力必然成为新的发展趋势。在二十大上国家也明确指出:“加快发展数字经济,促进数字经济和实体经济深度融合,打造具有国际经济力的数字产业集群。”那么对于广大企业来说,以后谋求数字化转型,不但需要注重打造线上体验,还需要与线下实体经济形成集群互动,打造更为畅通整体的数字化体系。一“在这样的需求背景,Sitecore这种可以协同线下线上、灵活扩展、快速地交付的CMS平台自然更受到企业的欢迎。这一发展势头从我们近期收到的咨询和实施项目数量就能明显地感觉到,”有着十余年Sitecore实施开发经验的睿哲信息如是说到,它对Sitecore未来前景也充满信心:“企业谋求数字化转型,都需要先搭建一个CMS平台作为根基,而Sitecore的个性化体验、全渠道数据洞察、自动数字营销这些突出的优势正是企业急需,可以实打实地助力企业完成数字化转型,实现市场收益、品牌知名度提升,它将来必然会被更多的企业选择,于我们来说,这也是促进客户增长、提高企业营收的好事。”——睿哲信息二作为一款专注于打造丰富客户体验的CMS平台,Sitecore功能涵盖了从内容到电子商务的方方面面,它可以集中管理你全渠道数字资产、它可以用个性化地内容培养用户关系、它可以打造互联的客户旅程等等,它的多重功能特性,让它更有能力去推动企业数字化转型、推动数实融合上。1.Sitecore将数字化营销贯彻到各个渠道,推动数实融合Sitecore具有出色的洞察力,它可以在线上帮企业去采集大量的用户行为数据(包括访客渠道来源、浏览内容),形成不断准确的用户画像;在用户画像的基础上依据不同渠道的特性进行个性化内容推送,培育客户对某个商品的兴趣和购买意愿。通过线上线下的数据整合,Sitecore的企业客户能够建立销售漏斗数据实时化展示,帮助企业了解到从广告曝光到实际购买的整个销售漏斗每个阶段的转化率。销售漏斗的数据进一步驱动每一个销售阶段的转化率优化,为Sitecore的客户带来可量化的效益。这样完整的一个流程,就实现全渠道融合营销,达到真正的“端到端”覆盖消费者。2.Sitecore具有多样附加模块功能,以无缝数字营销实现线下线上协同Sitecore拥有一系列附加模块,可确保用户在每个渠道中享受互联的客户体验。你可以实时将电子邮件营销与其他渠道集成,从而更容易在合适的时间提供正确的电子邮件优惠;你可以在社交网络(Facebook,LinkedIn,Instagram)上分享内容,并衡量付费社交活动的成功与否;你可以创建和发布外观精美的印刷内容,并根据他们搜索或查看的内容或他们告诉你他们感兴趣的内容,对你的意向客户实施更深入的个性化营销。3.易于部署,Sitecore让开发人员轻松应对复杂问题当我们判断一个CMS平台是不是好用的时候,是否容易开发也是一大标准,而对于Sitecore来说,睿哲信息为我们总结了其六大优势:“Sitecore更易于部署、EXM功能让它更易扩展、它的设计和内容分开既保证了安全性也使其平台易于开发管理、Sitecore允许开发人员更快执行他们的工作、Sitecore严格的安全模式保证了其在安全中更顺利开展开发工作、Sitecore支持容器化,让部署和运行更加容易……三对于企业来说,成功的数字化,意味着企业可以轻松地管理所有渠道上的数字资产,尽可能的洞察用户个人旅程中的数据、在全渠道上触达他们的用户,进而以自动的数字化营销推动个性化的体验的生成,这是一套系统而完整的数字化逻辑,需要一个功能强大的CMS平台的支撑,而Sitecore无论是从功能上、运行逻辑还是实施开发上,都具备了这样的能力。“数字化转型也好,数实互融也罢,最终追求的都是吸引用户、留住用户,而目前来说Sitecore这种可以提供的个性化体验的CMS平台是可以实现这一目标。随着数字化发展更加纵深,我们更需要以Sitecore来打好地基,其他的事才能徐徐图之。显而易见的是很多企业已经意识到了这件事,未来Sitecore必然在市场上更受欢迎。对于我们睿哲信息这种经验丰富的实施者来说,我们当然乐于见到这种需求趋势。我们也相信Sitecore可以更快地、也更好地推动企业实现数实融合的数字化转型。”——睿哲信息
文章
搜索推荐  ·  安全  ·  容器
2023-03-27
1
...
7 8 9 10 11 12
...
20
跳转至:
阿里云云原生
8802 人关注 | 7234 讨论 | 2087 内容
+ 订阅
  • 让应用交付和管理统一:KubeVela 亮点功能及核心技术回顾
  • 统一观测丨使用 Prometheus 监控 Nginx Ingress 网关最佳实践
  • 【送猫超卡、阿里云代金券】动手体验 SAE+云效 10 分钟快速打通 CI/CD 流水线
查看更多 >
云原生
234326 人关注 | 11613 讨论 | 47413 内容
+ 订阅
  • CSS块格式化上下文(Block Formatting Context,BFC)
  • CSS浮动
  • Spring框架尝鲜
查看更多 >
开发与运维
5786 人关注 | 133444 讨论 | 319613 内容
+ 订阅
  • Vue3+TypeScript学习笔记(一)
  • 什么是PHP设计模式?底层原理是什么?
  • Handler消息传递机制浅析
查看更多 >
安全
1247 人关注 | 24148 讨论 | 85930 内容
+ 订阅
  • Handler消息传递机制浅析
  • 网站使用了HTTPS之后,速度会变慢,底层原理是什么?
  • HTTP和HTTPS的区别是什么?底层原理是什么?
查看更多 >
数据库
252947 人关注 | 52318 讨论 | 99299 内容
+ 订阅
  • MySQL5.6如何实现全文搜索?具体步骤是怎样的?底层原理是什么?
  • 如何在MySQL中优化表性能?底层原理是什么?
  • 如何在MySQL中优化表性能?
查看更多 >