分布式文件系统FastDFS安装部署(高可用)
本文会搭建一个适合低业务访问业务量的高可用的FastDFS集群环境:两个Tracker服务,一个storage group中两个storage服务节点;该方案仅适用于业务访问量较低的环境下。对于大量业务系统的高并发访问,为了保证存储系统正常工作一般的架构思路:安装多个Tracker服务(至少两个,根据业务量调整),搭建多个storage group(至少两个,根据业务量调整),每个storage group中多个storage node(至少两个,做数据的冗余备份,进行容灾机制,而且node必须在不同的机器上)
一、FastDFS简介
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。
存储节点存储文件,完成文件管理的所有功能:就是这样的存储、同步和提供存取接口,FastDFS同时对文件的metadata进行管理。所谓文件的meta data就是文件的相关属性,以键值对(key value)方式表示,如:width=1024,其中的key为width,value为1024。文件metadata是文件属性列表,可以包含多个键值对。
跟踪器和存储节点都可以由一台或多台服务器构成。跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。
为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。
当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。
FastDFS中的文件标识分为两个部分:卷名和文件名,二者缺一不可。
———简介摘自百度百科
二、FastDFS原理介绍
1.文件上传
FastDFS以客户端库的方式提供基本的文件访问接口如upload、download、append、delete等,Storage 服务会定时的向Tracker服务发送自己的存储信息。当Tracker 服务集群中的Tracker 服务是多个时,各个Tracker服务之间的关系是对等的,因此客户端上传时会任意选择一个Trackre服务。当Tracker服务收到客户端上传文件请求时,会为该文件分配一个可以存储文件的group,当选定了group后就要决定给客户端分配group中的哪一个storage服务。当分配好storage 服务后,客户端向storage发送写文件请求,storage将会为文件分配一个数据存储目录。然后为文件分配一个文件ID标示,然后根据以上的信息生成文件名存储文件。
2.文件同步
上传文件后,客户端将文件写到group内的一个storage 服务即为上传文件成功,storage服务写完文件后,会由后台线程将文件同步至同group内的其他的storage 服务节点上。 每个storage服务写文件后,会同时写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内的所有server的始终保持同步。最后Storage服务的同步进度会作为元数据的一部分汇报到tracker服务上,tracker服务在选择读storage的时候会以同步进度作为参考指标。
3.下载文件
当下载文件时,客户端先询问tracker服务下载文件的storage,参数为文件标识(卷名和文件名);然后tracker向客户端返回一台可用的storage;最后客户端直接和storage通讯完成文件下载。
三、部署环境准备
1.环境说明
操作系统CentOS7.6
fastdfs版本:6.01
nginx版本:1.16.1
keepalived版本:2.0.19
2.系统依赖
gcc gcc-c++ perl pcre pcre-devel zlib zlib-devel openssl openssl-devel
3.软件环境
libevent下载地址:http://libevent.org/
nginx下载地址:http://nginx.org/en/download.html
keepalived下载地址:https://www.keepalived.org/software/
fastdfs下载地址:https://github.com/happyfish100/fastdfs/releases
libfasttcommon下载地址:https://github.com/happyfish100/libfastcommon/releases
fastdfs-nginx-module下载地址:https://github.com/happyfish100/fastdfs-nginx-module/releases
4.机器及网络环境规划
Fdfs Server VIP: 192.168.100.110 Tracker Server1: 192.168.100.111 Tracker Server2: 192.168.100.112 Storage Group1 Node1: 192.168.100.111 Storage Group1 Node2: 192.168.100.112
5.防火墙设置
关闭系统防火墙:sudo systemctl stop firewalld && systemctl disable firewalld
四、Keepalived服务安装配置
1.下载Keepalived源码包
官网地址:https://www.keepalived.org/
下载地址:https://www.keepalived.org/software/keepalived-2.0.19.tar.gz
2.上传并解压Keepalived源码包
tar -zxvf keepalived-2.0.19.tar.gz
3.编译Keepalived准备
进入解压目录:cd keepalived-2.0.19
执行编译准备:./configure --prefix=/work/keepalived
注意:一定要有gcc和openssl编译相关的依赖
4.编译安装Keepalived
make && make install
5.安装配置Keepalived
keepalived启动时会从/etc/keepalived/中相关的目录下查找keepalived.conf配置文件,因此将keepalived安装录/usr/local/keepalived/etc/keepalived.conf 拷贝到/etc/keepalived/中。
mkdir /etc/keepalived/
cp /work/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf
cp /work/keepalived/etc/sysconfig/keepalived /etc/sysconfig/keepalived
6.设置Keepalived开机启动项
systemctl enable keepalived
然后就能使用systemctl start/stop/status keepalived管理keepalived了
7.配置Keepalived服务
192.168.100.111机器上配置:vi /etc/keepalived/keepalived.conf
vrrp_script check_nginx { interval 3 script "/work/script/check_nginx.sh" } vrrp_instance fdfs_server { state MASTER interface enp0s3 virtual_router_id 110 priority 100 advert_int 3 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 192.168.100.110 } track_script { check_nginx } }
192.168.100.112机器上配置:vi /etc/keepalived/keepalived.conf
vrrp_script check_nginx { interval 3 script "/work/script/check_nginx.sh" } vrrp_instance fdfs_server { state BACKUP interface enp0s3 virtual_router_id 110 priority 90 advert_int 3 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 192.168.100.110 } track_script { check_nginx } }
8.编写nginx服务检测脚本
vi /work/script/check_nginx.sh
#!/bin/bash active_status=`netstat -lntp|grep nginx|wc -l` if [ $active_status -gt 0 ]; then exit 0 else exit 1 fi
然后给脚本赋予执行权限:chmod +x /work/script/check_nginx.sh
9.修改内核参数
vi /etc/sysctl.conf
增加如下内容:
net.ipv4.ip_nonlocal_bind = 1 #启动haproxy的时候,允许忽视VIP的存在 net.ipv4.ip_forward = 1 #允许转发
sysctl --system 使配置生效
五、安装FastDFS依赖库
1.安装libevent依赖
解压libevent源码包:tar -zxvf libevent-2.1.11-stable.tar.gz
进入源码目录:cd libevent-2.1.11-stable
编译安装前配置:./configure
编译安装:make && make install
默认安装位置:/usr/local/lib
2.安装libfasttcommon依赖
解压libfasttcommon源码包:tar -zxvf libfastcommon-1.0.41.tar.gz
进入源码目录:cd libfastcommon-1.0.41
编译安装:./make.sh && ./make.sh install
默认安装位置:/usr/lib64
六、安装部署Tracker服务和Storage服务
1.安装fastdfs服务
解压fastdfs源码包:tar -zxvf fastdfs-6.01.tar.gz
进入fastdfs源码包:cd fastdfs-6.01
编译安装:./make.sh && ./make.sh install
2.fastdfs服务目录信息
安装完成后服务及脚本拷贝到/usr/bin 目录,配置文件拷贝到/etc/fdfs目录,启动脚本拷贝到/etc/init.d/目录
3.注册开机启动
chkconfig --add fdfs_trackerd
chkconfig fdfs_trackerd on
chkconfig --add fdfs_storaged
chkconfig fdfs_storaged on
4.数据目录规划
创建fdfs数据主目录:mkdir /work/fdfs
创建tracker数据目录:mkdir /work/fdfs/tracker
创建storage数据目录:mkdir /work/fdfs/storage
5.配置tracker服务
将/etc/fdfs目录下的tracker.conf.sample改为tracker.conf:mv tracker.conf.sample tracker.conf修改内容如下:
将base_path=/home/yuqing/fastdfs 改为:/work/fdfs/tracker(该目录为上面定义创建)
启动Tracker服务:systemctl start fdfs_trackerd
6.配置storage服务
将/etc/fdfs目录下的storage.conf.sample改为storage.conf:mv storage.conf.sample storage.conf修改内容如下:
base_path=/home/yuqing/fastdfs 改为:base_path=/work/fdfs/storage(该目录为上面定义创建)
store_path0=/home/yuqing/fastdfs 改为:store_path0=/work/fdfs/storage(该目录为上面定义创建)
tracker_server=192.168.209.121:22122 改为:tracker_server=192.168.100.111:22122和tracker_server=192.168.100.112:22122
将/etc/fdfs/torage_ids.conf.sample 为storage_ids.conf,内容修改为当前group的storage节点信息:
100001 group1 192.168.100.111
100002 group1 192.168.100.112
启动Storage服务:systemctl start fdfs_storaged
七、Nginx服务安装配置
1.下载Nginx源码包
官网地址:http://nginx.org/
下载地址:http://nginx.org/en/download.html
2.上传并解压Nginx源码包及fastdfs插件包
tar -zxvf nginx-1.16.1.tar.gz
tar -zxvf fastdfs-nginx-module-1.21.tar.gz
3.编译Nginx准备
进入解压目录:cd nginx-1.16.1
拷贝fastdfs插件包到nginx源码目录:mv ../fastdfs-nginx-module-1.21 .
执行编译准备:./configure --prefix=/work/nginx \--with-stream \--add-module=fastdfs-nginx-module-1.21/src
注意:一定要有gcc和openssl编译相关的依赖
4.编译安装Nginx
make && make install
5.注册到系统服务
vi /usr/lib/systemd/system/nginx.service
[Unit] Description=nginx Documentation=http://nginx.org/en/docs/ After=network.target [Service] Type=forking PIDFile=/work/nginx/logs/nginx.pid ExecStartPre=/work/nginx/sbin/nginx -t -c /work/nginx/conf/nginx.conf ExecStart=/work/nginx/sbin/nginx -c /work/nginx/conf/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target
6.设置Nginx开机启动项
systemctl enable nginx
然后就能使用systemctl start/stop/status nginx管理nginx了
7.修改Nginx配置
vi /work/nginx/conf/nginx.conf
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } stream { upstream tracker { server 192.168.100.111:22122 weight=1 max_fails=2 fail_timeout=10s; server 192.168.100.112:22122 weight=1 max_fails=2 fail_timeout=10s; } server { listen 7777; proxy_timeout 5m; proxy_pass tracker; proxy_connect_timeout 10s; } } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; upstream storage { server 192.168.100.111:8888 weight=1 max_fails=2 fail_timeout=10s; server 192.168.100.112:8888 weight=1 max_fails=2 fail_timeout=10s; } server { listen 80; server_name localhost; location /group1 { proxy_pass http://storage; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } server { listen 8888; server_name localhost; location / { alias /work/fdfs/storage/data/; ngx_fastdfs_module; } } }
8.配置nginx的fdfs插件
将/etc/fdfs下的http.conf.sample和mime.types.sample重命名为:http.conf和mime.types
将fastdfs-nginx-module-1.21/src下的mod_fastdfs.conf拷贝到/etc/fdfs下
修改mod_fastdfs.conf如下:
连接超时时间: connect_timeout=5
Tracker服务地址:tracker_server=192.168.100.111:22122 和tracker_server=192.168.100.112:22122
Storage服务端口:storage_server_port=23000
如果文件ID的uri中包含/group**,则要设置为true:url_have_group_name = true
Storage配置的store_path0路径,必须和storage.conf中的一致:store_path0=/work/fdfs/storage
其他详细配置如下:
# connect timeout in seconds # default value is 30s connect_timeout=5 # network recv and send timeout in seconds # default value is 30s network_timeout=10 # the base path to store log files base_path=/work/fdfs/storage # if load FastDFS parameters from tracker server # since V1.12 # default value is false load_fdfs_parameters_from_tracker=true # storage sync file max delay seconds # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # since V1.12 # default value is 86400 seconds (one day) storage_sync_file_max_delay = 86400 # if use storage ID instead of IP address # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # default value is false # since V1.13 use_storage_id = false # specify storage ids filename, can use relative or absolute path # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # since V1.13 storage_ids_filename = storage_ids.conf # FastDFS tracker_server can ocur more than once, and tracker_server format is # "host:port", host can be hostname or ip address # valid only when load_fdfs_parameters_from_tracker is true tracker_server=192.168.100.111:22122 tracker_server=192.168.100.112:22122 # the port of the local storage server # the default value is 23000 storage_server_port=23000 # the group name of the local storage server group_name=group1 # if the url / uri including the group name # set to false when uri like /M00/00/00/xxx # set to true when uri like ${group_name}/M00/00/00/xxx, such as group1/M00/xxx # default value is false url_have_group_name = true # path(disk or mount point) count, default value is 1 # must same as storage.conf store_path_count=1 # store_path#, based 0, if store_path0 not exists, it's value is base_path # the paths must be exist # must same as storage.conf store_path0=/work/fdfs/storage # standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info # set the log filename, such as /usr/local/apache2/logs/mod_fastdfs.log # empty for output to stderr (apache and nginx error_log file) log_filename= # response mode when the file not exist in the local file system ## proxy: get the content from other storage server, then send to client ## redirect: redirect to the original storage server (HTTP Header is Location) response_mode=proxy # the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a # multi aliases split by comma. empty value means auto set by OS type # this paramter used to get all ip address of the local host # default values is empty if_alias_prefix= # use "#include" directive to include HTTP config file # NOTE: #include is an include directive, do NOT remove the # before include #include http.conf # if support flv # default value is false # since v1.15 flv_support = true # flv file extension name # default value is flv # since v1.15 flv_extension = flv # set the group count # set to none zero to support multi-group on this storage server # set to 0 for single group only # groups settings section as [group1], [group2], ..., [groupN] # default value is 0 # since v1.14 group_count = 1 # group settings for group #1 # since v1.14 # when support multi-group on this storage server, uncomment following section [group1] group_name=group1 storage_server_port=23000 store_path_count=1 store_path0=/work/fdfs/storage # group settings for group #2 # since v1.14 # when support multi-group, uncomment following section as neccessary #[group2] #group_name=group2 #storage_server_port=23000 #store_path_count=1 #store_path0=/home/yuqing/fastdfs
八、服务启动及验证
分别启动keepalive、nginx、tracker、storage服务
查看服务是否正常服务:
在任意Storage机器上查看集群状态:fdfs_monitor /etc/fdfs/storage.conf
九、Java客户端测试
1.java项目Maven依赖
项目地址:https://github.com/tobato/FastDFS_Client 目前客户端主要依赖于SpringBoot,因此必须引入: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> </parent> FastDFS 依赖包: <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.26.7</version> </dependency> 将FastDFS引入项目: @Import(FdfsClientConfig.class) 在application.yml当中配置Fdfs相关参数: fdfs: pool: #连接池最大数量 max-total: 200 #每个tracker地址的最大连接数 max-total-per-key: 50 #连接耗尽时等待获取连接的最大毫秒数 max-wait-millis: 5000 so-timeout: 1500 connect-timeout: 600 thumb-image: width: 150 height: 150 tracker-list: - 192.168.100.110:7777 或者 fdfs: pool: #连接池最大数量 max-total: 200 #每个tracker地址的最大连接数 max-total-per-key: 50 #连接耗尽时等待获取连接的最大毫秒数 max-wait-millis: 5000 so-timeout: 1500 connect-timeout: 600 thumb-image: width: 150 height: 150 tracker-list: - 192.168.100.111:22122 - 192.168.100.112:22122 使用接口服务对Fdfs服务端进行操作,主要接口包括: TrackerClient - TrackerServer接口 GenerateStorageClient - 一般文件存储接口 (StorageServer接口) FastFileStorageClient - 为方便项目开发集成的简单接口(StorageServer接口) AppendFileStorageClient - 支持文件续传操作的接口 (StorageServer接口)
2.代码测试
package com.maxbill; import com.github.tobato.fastdfs.FdfsClientConfig; import com.github.tobato.fastdfs.domain.fdfs.MetaData; import com.github.tobato.fastdfs.domain.fdfs.StorePath; import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray; import com.github.tobato.fastdfs.service.FastFileStorageClient; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.io.File; import java.io.FileInputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; @Log4j2 @Component @Import(FdfsClientConfig.class) public class FdfsClientUtil { private static final String BASE_URL = "http://192.168.100.110:8888/"; @Autowired private FastFileStorageClient storageClient; private static FdfsClientUtil fdfsClientUtil; @PostConstruct public void init() { fdfsClientUtil = this; } /** * 文件上传 * * @param file 文件信息 * @param infoMap 文件扩展信息 * @return 上传路径 */ public static Map<String, Object> uploadFile(File file, Map<String, String> infoMap) { try { String fileName = file.getName(); String fileType = fileName.substring(fileName.lastIndexOf("\\") + 1); //String fileType = FilenameUtils.getExtension(file.getName()) log.info("[fdfs-upload]-start upload file ... "); log.info("[fdfs-upload]-request upload file name: {}", fileName); log.info("[fdfs-upload]-request upload file info: {}", infoMap); StorePath path = fdfsClientUtil.storageClient.uploadFile(new FileInputStream(file), file.length(), fileType, getMetaData(infoMap)); log.info("[fdfs-upload]-upload success path: {}", path.getFullPath()); return getResultMap(BASE_URL.concat(path.getFullPath()), null); } catch (Exception e) { log.error("[fdfs-upload]-upload file exception info: {}", e.getMessage()); return getResultMap(null, e.getMessage()); } } /** * 下载文件 * * @param filePath 文件路径标识 * @return 文件字节 */ public static Map<String, Object> downloadFile(String filePath) { try { filePath = filePath.replace(BASE_URL, ""); StorePath storePath = StorePath.parseFromUrl(filePath); String group = storePath.getGroup(); String path = storePath.getPath(); log.info("[fdfs-download]-start download file ... "); log.info("[fdfs-download]-request download file group: {}", group); log.info("[fdfs-download]-request download file path: {}", path); byte[] data = fdfsClientUtil.storageClient.downloadFile(group, path, new DownloadByteArray()); log.info("[fdfs-download]-request download file success ... "); return getResultMap(data, null); } catch (Exception e) { log.error("[fdfs-download]-download file exception info: {}", e.getMessage()); return getResultMap(null, e.getMessage()); } } /** * 删除文件 * * @param filePath 文件路径标识 * @return 操作结果 */ public static boolean deleteFile(String filePath) { try { filePath = filePath.replace(BASE_URL, ""); StorePath storePath = StorePath.parseFromUrl(filePath); String group = storePath.getGroup(); String path = storePath.getPath(); log.info("[fdfs-delete]-start delete file ... "); log.info("[fdfs-delete]-request delete file group: {}", group); log.info("[fdfs-delete]-request delete file path: {}", path); fdfsClientUtil.storageClient.deleteFile(storePath.getGroup(), storePath.getPath()); log.info("[fdfs-delete]-request delete file success ... "); return true; } catch (Exception e) { log.error("[fdfs-delete]-delete file exception info: {}", e.getMessage()); return false; } } /** * 查看文件元信息 * * @param filePath 文件路径标识 * @return 文件信息 */ public static Map<String, Object> getFileInfo(String filePath) { try { filePath = filePath.replace(BASE_URL, ""); StorePath storePath = StorePath.parseFromUrl(filePath); String group = storePath.getGroup(); String path = storePath.getPath(); log.info("[fdfs-meta]-start meta file ... "); log.info("[fdfs-meta]-request meta file group: {}", group); log.info("[fdfs-meta]-request meta file path: {}", path); Map<String, String> infoMap = new HashMap<>(); infoMap.put("createPath", filePath); Set<MetaData> metaData = fdfsClientUtil.storageClient.getMetadata(storePath.getGroup(), storePath.getPath()); log.info("[fdfs-meta]-request meta file success ... "); if (null != metaData && !metaData.isEmpty()) { metaData.forEach(meta -> { infoMap.put(meta.getName(), meta.getValue()); }); } return getResultMap(infoMap, null); } catch (Exception e) { log.error("[fdfs-meta]-meta file exception info: {}", e.getMessage()); return getResultMap(null, e.getMessage()); } } /** * 封装附件元信息 * * @param infoMap 自定义数据 * @return 附件元信息 */ private static Set<MetaData> getMetaData(Map<String, String> infoMap) { if (null != infoMap && !infoMap.isEmpty()) { Set<MetaData> metaDataSet = new HashSet<>(); for (String key : infoMap.keySet()) { metaDataSet.add(new MetaData(key, infoMap.get(key))); } return metaDataSet; } else { DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Set<MetaData> metaDataSet = new HashSet<>(); metaDataSet.add(new MetaData("createUser", "HuiZhouCMS")); metaDataSet.add(new MetaData("createDate", df.format(new Date()))); return metaDataSet; } } /** * 封装结果信息 * * @param data 数据 * @param info 信息 * @return 操作结果 */ private static Map<String, Object> getResultMap(Object data, String info) { Map<String, Object> resultMap = new HashMap<>(); if (StringUtils.isEmpty(info)) { resultMap.put("flag", true); resultMap.put("data", data); resultMap.put("info", "success"); } else { resultMap.put("flag", false); resultMap.put("info", info); resultMap.put("data", null); } return resultMap; } }
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
高手问答第 236 期 —— 如何让微服务真正落地?
以往的软件应用都是单块应用,随着用户和流量增加,单块应用无法支持,而且复杂的单块应用也难以维护、难以测试。微服务架构希望把服务拆分打包装进容器来解决这些问题,然而大家发现微服务的运维工作量不是简单地按照服务的数量线性增加,而是按照服务数量的平方增加。可想而知,如果不想办法降低运维成本,微服务就成了不切实际的空中楼阁。那么如何让微服务真正落地呢?今天我们就来聊聊这个话题。 OSCHINA 本期高手问答(11 月 26日 - 12月 03日)将围绕「微服务运维」展开讨论,问答范围可包括但不限于:DevOps 到底是什么;实施 DevOps 所面临的挑战;DevOps 与微服务的关系等等。有其他相关的问题,也欢迎提问。为此,我们邀请到了畅销书《微服务运维实战》的译者汪欣@太空行走 老师。 嘉宾简介 汪欣,《微服务运维实践》译者。吉林大学计算机专业,有二十年的软件开发经验,多年担任知名外企系统架构师。熟悉软件开发流程,项目管理和架构设计。 为了鼓励踊跃提问,华中科技大学出版社会在问答结束后从提问者中抽取5名幸运会员赠予《微服务运维实战(第二卷)》一书。 购书地址:http://mrw.so/5...
- 下一篇
Java 8 Stream Api 中的 peek 操作
1. 前言 我在Java8 Stream API 详细使用指南 中讲述了 Java 8 Stream API 中 map 操作和 flatMap 操作的区别。然后有小伙伴告诉我 peek 操作 也能实现元素的处理。但是你知道 map 和 peek 的区别吗? map 我们在开头文章已经讲过了,你可以去详细了解一下它,本文将重点讲解一下 peek 操作。 2. peek peek 操作接收的是一个 Consumer<T> 函数。顾名思义 peek 操作会按照 Consumer<T> 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性。 这里我们要提一下这个 Consumer<T> 以理解 什么时消费。 2.1 什么是消费 (Consumer) package java.util.function; import java.util.Objects; @FunctionalInterface public interface Consumer<T> { void accept(T t); // 嵌套accept , 顺序为...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS关闭SELinux安全模块
- CentOS7,CentOS8安装Elasticsearch6.8.6