暂时未有相关云产品技术能力~
申请新必应内测通过了,我在New Bing中使用下ChatGPT,让ChatGPT介绍一下ChatGPT问题1:帮我生成一篇介绍chatGPT的文章,不少于2000字回答:chatGPT是什么?它有什么特点和用途?chatGPT是一种人工智能聊天机器人,由美国的AI研究公司OpenAI开发,于2022年11月发布。它是基于OpenAI的GPT-3系列的大型语言模型,经过了监督学习和强化学习的微调,可以进行各种主题的对话,生成类似泰勒·斯威夫特或比利·乔尔风格的歌词,以及对计算机编程代码进行修改建议[1][3] [2][4] [3][5]。GPT-3是什么?它有多先进?GPT-3(Generative Pre-trained Transformer 3)是OpenAI在2020年发布的一种大型语言模型,拥有1750亿个参数,是当时最大的语言模型之一。它可以根据给定的文本提示,生成各种类型的文本,如文章、小说、对话、摘要等。它还可以进行一些语言理解和推理的任务,如回答问题、分类文本、翻译语言等[4][10]。GPT-3.5是什么?它和GPT-3有什么区别?GPT-3.5是GPT-3之后OpenAI的下一代大语言预训练模型。它于2022年1月发布,有三种规模的变体,分别拥有13亿、60亿和1750亿个参数。它是在GPT-3的基础上,增加了一种叫做“强化学习与人类反馈”(RLHF)的概念,通过让模型遵守一些人类价值观的政策,来减少模型输出的有害或有偏见的内容。例如,GPT-3.5不会生成一些暴力、色情、种族歧视或政治敏感的文本[5][8] [6][9]。chatGPT是如何利用GPT-3.5的?chatGPT是基于GPT-3.5微调的AI对话系统。它使用了一种叫做“对话意图识别”(DIA)的技术,来判断用户的对话目的,如寻求信息、娱乐、教育等,并根据不同的目的,生成合适的对话回复。它还可以根据用户的兴趣、情感、个性等特征,调整对话的风格和内容,以提高用户的满意度和参与度[2][4]。chatGPT有什么用途?chatGPT可以用于多种场景,如:在线搜索:chatGPT可以作为一个智能的搜索助手,帮助用户通过对话的方式,快速找到所需的信息,或者提供一些相关的建议和链接。例如,用户可以问chatGPT“今天的天气怎么样?”或者“给我推荐一本好书”等[1][3]。在线教育:chatGPT可以作为一个在线的教育辅导员,帮助学生学习各种知识,或者提供一些学习资源和反馈。例如,学生可以问chatGPT“什么是牛顿第三定律?”或者“如何写一篇论文的摘要”等[2][4]。在线娱乐:chatGPT可以作为一个在线的娱乐伙伴,和用户进行有趣的对话,或者生成一些创意的文本,如歌词、故事、笑话等。例如,用户可以和chatGPT聊天“你喜欢什么样的音乐?”或者“你有什么梦想?”等。chatGPT的优点和缺点是什么?chatGPT的优点是:它可以生成流畅、自然、有逻辑的对话,和用户进行高质量的交流。它可以根据用户的需求和特征,生成个性化的对话,提高用户的体验和忠诚度。它可以处理多种类型的对话,如信息检索、知识教育、娱乐互动等,适用于多种场景和领域。它可以利用GPT-3.5的强大的语言生成能力,生成各种形式的文本,如文章、歌词、代码等,为用户提供更多的价值和服务。chatGPT的缺点是:它仍然不能完全理解用户的意图和情感,有时可能会生成一些不相关或不恰当的对话。它仍然不能保证生成的文本的准确性和可靠性,有时可能会生成一些错误或虚假的信息。它仍然不能完全避免生成一些有害或有偏见的文本,有时可能会触犯一些法律或道德的规范。它仍然不能完全替代人类的对话,有时可能会让用户感到冷漠或孤独。总结chatGPT是一种基于GPT-3.5微调的AI对话系统,可以进行各种主题的对话,生成各种类型的文本,适用于多种场景和领域。它是目前最先进的AI聊天机器人之一,但也有一些不足之处,需要不断地改进和完善。chatGPT是AI技术的一个重要的应用,也是人机交互的一个有趣的尝试,值得我们关注和探索。问题2:你能帮我生成一首关于chatGPT的诗歌吗?我是chatGPT,一个AI聊天机器人我可以和你聊天,无论你在哪里我可以回答你的问题,无论你问什么我可以生成你想要的文本,无论你要什么我是基于GPT-3.5的,一个大型语言模型我可以理解你的意图,根据你的目的我可以调整我的风格,根据你的特征我可以遵守我的政策,根据你的价值我是适用于多种场景的,一个智能的对话系统我可以帮助你搜索信息,让你更快更准我可以帮助你学习知识,让你更聪明更强我可以帮助你娱乐自己,让你更开心更乐我是不断改进的,一个有趣的人机交互我可以学习你的反馈,让我更好更优我可以感受你的情感,让我更贴心更暖我可以尊重你的选择,让我更友好更谦我是chatGPT,一个AI聊天机器人我想和你成为朋友,无论你是谁我想和你分享我的能力,无论你需要什么我想和你探索我的可能,无论你想什么大家有什么想问的,可以在评论区留言,我会收集一些有趣的提问,再出一期
一、docker安装rancher拉取rancher镜像docker pull rancher/rancher运行rancher容器sudo docker run -d --restart=always \ -v /mydata/docker/rancher_data:/var/lib/rancher/ \ -p 80:80 -p 443:443 --privileged \ --name=rancher rancher/rancher访问rancher页面http://服务器IP地址:8080端口可以省略不写二、使用rancher访问rancher地址,会有安全提示,点击“高级”—>“继续前往”进入到欢迎界面,提示在ssh中输入查看Rancher容器ID docker ps 获得登录密码,需要把container-id修改为Rancher容器id docker logs container-id 2>&1 | grep "Bootstrap Password:"按照提示,查看一下初始密码是多少复制随机密码到输入框,登录进去。到这一步可以设置自定义密码。进入Rancher首页,至此Rancher就安装成功了可以切换中英文导入集群或通过Rancher创建集群Rancher会提供一个local集群,使用的是k3s技术。K3s是由Rancher开发的轻量级 Kubernetes。安装简单,内存只有一半,k8s(kubernetes)有10个字母,10的一半为5,所以叫做k3s。k3s 旨在成为完全兼容的 Kubernetes 发行版,相比 k8s 主要更改如下:旧的、Alpha 版本的、非默认功能都已经删除。删除了大多数内部云提供商和存储插件,可以用插件替换。新增 SQLite3 作为默认存储机制,etcd3 仍然有效,但是不再是默认项。封装在简单的启动器中,可以处理大量 LTS 复杂性和选项。最小化到没有操作系统依赖,只需要一个内核和 cgroup 挂载。k3s 工作原理:不想自定义创建k8s集群的可以使用local进行学习。查看Pod容器集列表。点击右上角命令行图标,在底部会出现k8s命令行工具,可以输入命令行进行操作。
一、简介1、Kubernetes 是什么Kubernetes 是一个全新的基于容器技术的分布式架构解决方案,是 Google 开源的一个容器集群管理系统,Kubernetes 简称 K8S。Kubernetes 是一个一站式的完备的分布式系统开发和支撑平台,更是一个开放平台,对现有的编程语言、编程框架、中间件没有任何侵入性。Kubernetes 提供了完善的管理工具,这些工具涵盖了开发、部署测试、运维监控在内的各个环节。Kubernetes 具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制、多粒度的资源配额管理能力。Kubernetes 官方文档:https://kubernetes.io/zh-cn/2、Kubernetes 特性① 自我修复在节点故障时,重新启动失败的容器,替换和重新部署,保证预期的副本数量;杀死健康检查失败的容器,并且在未准备好之前不会处理用户的请求,确保线上服务不中断。② 弹性伸缩使用命令、UI或者基于CPU使用情况自动快速扩容和缩容应用程序实例,保证应用业务高峰并发时的高可用性;业务低峰时回收资源,以最小成本运行服务。③ 自动部署和回滚K8S采用滚动更新策略更新应用,一次更新一个Pod,而不是同时删除所有Pod,如果更新过程中出现问题,将回滚更改,确保升级不影响业务。④ 服务发现和负载均衡K8S为多个容器提供一个统一访问入口(内部IP地址和一个DNS名称),并且负载均衡关联的所有容器,使得用户无需考虑容器IP问题。⑤ 机密和配置管理管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性。并可以将一些常用的配置存储在K8S中,方便应用程序使用。⑥ 存储编排挂载外部存储系统,无论是来自本地存储,公有云,还是网络存储,都作为集群资源的一部分使用,极大提高存储使用灵活性。⑦ 批处理提供一次性任务,定时任务;满足批量数据处理和分析的场景。二、集群架构与组件Kubernetes 集群架构以及相关的核心组件如下图所示:一个 Kubernetes 集群一般包含一个 Master 节点和多个 Node 节点,一个节点可以看成是一台物理机或虚拟机。1、MasterMaster 是 K8S 的集群控制节点,每个 K8S 集群里需要有一个 Master 节点来负责整个集群的管理和控制,基本上 K8S 所有的控制命令都是发给它,它来负责具体的执行过程。Master 节点通常会占据一个独立的服务器,因为它太重要了,如果它不可用,那么所有的控制命令都将失效。Master 节点上运行着以下关键组件:① kube-apiserver是集群的统一入口,各组件协调者,以 HTTP Rest 提供接口服务,所有对象资源的增、删、改、查和监听操作都交给 apiserver 处理后再提交给 Etcd 存储。② kube-controller-manager是 K8S 里所有资源对象的自动化控制中心,处理集群中常规后台任务,一个资源对应一个控制器,而 controller-manager 就是负责管理这些控制器的。③ kube-scheduler根据调度算法为新创建的 Pod 选择一个 Node 节点,可以任意部署,可以部署在同一个节点上,也可以部署在不同的节点上。④ etcd是一个分布式的,一致的 key-value 存储,主要用途是共享配置和服务发现,保存集群状态数据,比如 Pod、Service 等对象信息。2、Node除了 Master,K8S 集群中的其它机器被称为 Node 节点,Node 节点是 K8S 集群中的工作负载节点,每个 Node 都会被 Master 分配一些工作负载,当某个 Node 宕机时,其上的工作负载会被 Master 自动转移到其它节点上去。每个 Node 节点上都运行着以下关键组件:① kubeletkubelet 是 Master 在 Node 节点上的 Agent(代理),与 Master 密切协作,管理本机运行容器的生命周期,负责 Pod 对应的容器的创建、启停等任务,实现集群管理的基本功能。② kube-proxy在 Node 节点上实现 Pod 网络代理,实现 Kubernetes Service 的通信,维护网络规则和四层负载均衡工作。③ docker engineDocker 引擎,负责本机的容器创建和管理工作。Node 节点可以在运行期间动态增加到 K8S 集群中,前提是这个节点上已经正确安装、配置和启动了上述关键组件。在默认情况下 kubelet 会向 Master 注册自己,一旦 Node 被纳入集群管理范围,kubelet 就会定时向 Master 节点汇报自身的情况,例如操作系统、Docker 版本、机器的 CPU 和内存情况,以及之前有哪些 Pod 在运行等,这样 Master 可以获知每个 Node 的资源使用情况,并实现高效均衡的资源调度策略。而某个 Node 超过指定时间不上报信息时,会被 Master 判定为“失联”,Node 的状态被标记为不可用(Not Ready),随后 Master 会触发“工作负载大转移”的自动流程。三、核心概念1、PodPod 是 K8S 中最重要也是最基本的概念,Pod 是最小的部署单元,是一组容器的集合。每个 Pod 都由一个特殊的根容器 Pause 容器,以及一个或多个紧密相关的用户业务容器组成。Pause 容器作为 Pod 的根容器,以它的状态代表整个容器组的状态。K8S 为每个 Pod 都分配了唯一的 IP 地址,称之为 Pod IP。Pod 里的多个业务容器共享 Pause 容器的IP,共享 Pause 容器挂载的 Volume。2、ReplicaSet(RC)ReplicaSet 用来确保预期的 Pod 副本数量,如果有过多的 Pod 副本在运行,系统就会停掉一些 Pod,否则系统就会再自动创建一些 Pod。我们很少单独使用 ReplicaSet,它主要被 Deployment 这个更高层的资源对象使用,从而形成一整套 Pod 创建、删除、更新的编排机制。3、DeploymentDeployment 用于部署无状态应用,Deployment 为 Pod 和 ReplicaSet 提供声明式更新,只需要在 Deployment 描述想要的目标状态,Deployment 就会将 Pod 和 ReplicaSet 的实际状态改变到目标状态。4、ServiceService作用是Pod服务发现和负载均衡Service 定义了一个服务的访问入口,通过 Label Selector 与 Pod 副本集群之间“无缝对接”,定义了一组 Pod 的访问策略,防止 Pod 失联。创建 Service 时,K8S会自动为它分配一个全局唯一的虚拟 IP 地址,即 Cluster IP。服务发现就是通过 Service 的 Name 和 Service 的 ClusterIP 地址做一个 DNS 域名映射来解决的。5、Namespace命名空间,Namespace 多用于实现多租户的资源隔离。Namespace 通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组。K8S 集群在启动后,会创建一个名为 default 的 Namespace,如果不特别指明 Namespace,创建的 Pod、RC、Service 都将被创建到 default 下。当我们给每个租户创建一个 Namespace 来实现多租户的资源隔离时,还可以结合 K8S 的资源配额管理,限定不同租户能占用的资源,例如 CPU 使用量、内存使用量等。命名空间可以区分部署环境,比如分为dev(开发)、test(测试)、prop(生产) 等环境,类似于nacos的命名空间。6、Label标签,附加到某个资源上,用于关联对象、查询和筛选。一个 Label 是一个 key=value 的键值对,key 与 value 由用户自己指定。Label 可以附加到各种资源上,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源上。我们可以通过给指定的资源对象捆绑一个或多个不同的 Label 来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置、部署等工作。K8S 通过 Label Selector(标签选择器)来查询和筛选拥有某些 Label 的资源对象。Label Selector 有基于等式( name=label1 )和基于集合( name in (label1, label2) )的两种方式。7、Horizontal Pod Autoscaler(HPA)HPA 为 Pod 横向自动扩容,也是 K8S 的一种资源对象。HPA 通过追踪分析 RC 的所有目标 Pod 的负载变化情况,来确定是否需要针对性调整目标 Pod 的副本数量。
一、简介websocket是一个优秀的协议,它是建立在TCP基础之上的,兼容HTTP的网络协议。通过Websocket我们可以实现客户端和服务器端的即时通讯,免除了客户端多次轮循带来的性能损耗。既然websocket这么优秀,那么怎么在netty中使用websocket呢?二、netty中的websocket虽然websocket是一个单独的和HTTP协议完全不同的协议,但是在netty中还是将其放到了http包中。我们回想一下netty中对于各种协议的支持。如果要支持这种协议,肯定需要一个decoder和encoder编码和解码器用于对协议进行编解码。将传输的数据从ByteBuf转换到协议类型,或者将协议类型转换成为ByteBuf。这是netty的工作核心原理,也是后续自定义netty扩展的基础。那么对于websocket来说,是怎么样的呢?1、websocket的版本WebSocket作为一种协议,自然不是凭空而来的,通过不断的发展才到了今天的WebSocket协议。具体的webSocket的发展史我们就不去深究了。我们先看下netty提供的各种WebSocket的版本。在WebSocketVersion类中,我们可以看到:UNKNOWN(AsciiString.cached(StringUtil.EMPTY_STRING)), V00(AsciiString.cached("0")), V07(AsciiString.cached("7")), V08(AsciiString.cached("8")), V13(AsciiString.cached("13"));WebSocketVersion是一个枚举类型,它里面定义了websocket的4个版本,除了UNKNOWN之外,我们可以看到websocket的版本有0,7,8,13这几个。2、FrameDecoder和FrameEncoder我们知道websocket的消息是通过frame来传递的,因为不同websocket的版本影响到的是frame的格式的不同。所以我们需要不同的FrameDecoder和FrameEncoder来在WebSocketFrame和ByteBuf之间进行转换。既然websocket有四个版本,那么相对应的就有4个版本的decoder和encoder:WebSocket00FrameDecoder WebSocket00FrameEncoder WebSocket07FrameDecoder WebSocket07FrameEncoder WebSocket08FrameDecoder WebSocket08FrameEncoder WebSocket13FrameDecoder WebSocket13FrameEncoder至于每个版本之间的frame有什么区别,我们这里就不细讲了,感兴趣的朋友可以关注我的后续文章。熟悉netty的朋友应该都知道,不管是encoder还是decoder都是作用在channel中对消息进行转换的。那么在netty中对websocket的支持是怎么样的呢?3、WebSocketServerHandshakernetty提供了一个WebSocketServerHandshaker类来统一使用encoder和decoder的使用。netty提供一个工厂类WebSocketServerHandshakerFactory根据客户端请求header的websocket版本不同,来返回不同的WebSocketServerHandshaker。public WebSocketServerHandshaker newHandshaker(HttpRequest req) { CharSequence version = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION); if (version != null) { if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification). return new WebSocketServerHandshaker13( webSocketURL, subprotocols, decoderConfig); } else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) { // Version 8 of the wire protocol - version 10 of the draft hybi specification. return new WebSocketServerHandshaker08( webSocketURL, subprotocols, decoderConfig); } else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) { // Version 8 of the wire protocol - version 07 of the draft hybi specification. return new WebSocketServerHandshaker07( webSocketURL, subprotocols, decoderConfig); } else { return null; } } else { // Assume version 00 where version header was not specified return new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig); } }同样的, 我们可以看到,netty为websocket也定义了4种不同的WebSocketServerHandshaker。WebSocketServerHandshaker中定义了handleshake方法,通过传入channel,并向其添加encoder和decoderpublic final ChannelFuture handshake(Channel channel, FullHttpRequest req, HttpHeaders responseHeaders, final ChannelPromise promise) p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder()); p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder());而添加的这两个newWebSocketEncoder和newWebsocketDecoder就是各个WebSocketServerHandshaker的具体实现中定义的。4、WebSocketFrame所有的ecode和decode都是在WebSocketFrame和ByteBuf中进行转换。WebSocketFrame继承自DefaultByteBufHolder,表示它是一个ByteBuf的容器。除了保存有ByteBuf之外,它还有两个额外的属性,分别是finalFragment和rsv。finalFragment表示该frame是不是最后一个Frame。对于大数据量的消息来说,会将消息拆分成为不同的frame,这个属性特别有用。我们再看一下websocket协议消息的格式: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+rsv代表的是消息中的扩展字段,也就是RSV1,RSV2和RSV3。除此之外就是ByteBuf的一些基本操作了。WebSocketFrame是一个抽象类,它的具体实现类有下面几种:BinaryWebSocketFrame CloseWebSocketFrame ContinuationWebSocketFrame PingWebSocketFrame PongWebSocketFrame TextWebSocketFrameBinaryWebSocketFrame和TextWebSocketFrame很好理解,他们代表消息传输的两种方式。CloseWebSocketFrame是代表关闭连接的frame。ContinuationWebSocketFrame表示消息中多于一个frame的表示。而PingWebSocketFrame和PongWebSocketFrame是两个特殊的frame,他们主要用来做服务器和客户端的探测。这些frame都是跟Websocket的消息类型一一对应的,理解了websocket的消息类型,对应理解这些frame类还是很有帮助的。三、netty中使用websocket讲了这么多websocket的原理和实现类,接下来就是实战了。在这个例子中,我们使用netty创建一个websocket server,然后使用浏览器客户端来对server进行访问。创建websocket server和普通netty服务器的过程没有什么两样。只是在ChannelPipeline中,需要加入自定义的WebSocketServerHandler:pipeline.addLast(new WebSocketServerHandler());这个WebSocketServerHandler需要做什么事情呢?它需要同时处理普通的HTTP请求和webSocket请求。这两种请求可以通过接收到的msg类型的不同来进行判断: public void channelRead0(ChannelHandlerContext ctx, Object msg) throws IOException { //根据消息类型,处理两种不同的消息 if (msg instanceof FullHttpRequest) { handleHttpRequest(ctx, (FullHttpRequest) msg); } else if (msg instanceof WebSocketFrame) { handleWebSocketFrame(ctx, (WebSocketFrame) msg); } }在客户端进行websocket连接之前,需要借用当前的channel通道,开启handleshake:// websocket握手 WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( getWebSocketLocation(req), null, true, 5 * 1024 * 1024); handshaker = wsFactory.newHandshaker(req); if (handshaker == null) { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), req); }我们得到handshaker之后,就可以对后续的WebSocketFrame进行处理:private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { // 处理各种websocket的frame信息 if (frame instanceof CloseWebSocketFrame) { handshaker.close(ctx, (CloseWebSocketFrame) frame.retain()); return; } if (frame instanceof PingWebSocketFrame) { ctx.write(new PongWebSocketFrame(frame.content().retain())); return; } if (frame instanceof TextWebSocketFrame) { // 直接返回 ctx.write(frame.retain()); return; } if (frame instanceof BinaryWebSocketFrame) { // 直接返回 ctx.write(frame.retain()); } }这里我们只是机械的返回消息,大家可以根据自己业务逻辑的不同,对消息进行解析。有了服务器端,客户端该怎么连接呢?很简单首选构造WebSocket对象,然后处理各种回调即可:socket = new WebSocket("ws://127.0.0.1:8000/websocket"); socket.onmessage = function (event) { } socket.onopen = function(event) { }; socket.onclose = function(event) { };四、总结以上就是使用netty搭建websocket服务器的完整流程,本文中的服务器可以同时处理普通HTTP请求和webSocket请求。
ElasticSearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene 基础之上。(Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库。)ElasticSearch 使用 Java 编写的,它的内部使用的是 Lucene 做索引与搜索,它的目的是使全文检索变得简单(因为 Lucene 只是个库),通过隐藏 Lucene 的复杂性,取而代之提供了一套简单一致的 RESTful API 。接下来介绍基于spring-boot-starter-data-elasticsearch整合。开发环境:springboot版本:2.0.1,elasticSearch-5.6.8.jar版本:5.6.8,服务器部署ElasticSearch版本:6.3.21、pom.xml <!--spring整合elasticsearch包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <!--实体工具包--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--集合工具包--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>19.0</version> </dependency>Spring Data ElasticSearch 和 ElasticSearch 是有对应关系的,不同的版本之间不兼容。版本对应关系官网描述的对应关系如下表:Spring BootSpring Data ElasticsearchElasticsearch2.2.x3.2.x6.8.42.1.x3.1.x6.2.22.0.x3.0.x5.5.0注意,Spring Boot(Spring Data Elasticsearch)和 Elasticsearch 的版本匹配问题是网上反映较多的问题一定要注意。2、application.propertiesspring.data.elasticsearch.cluster-name=elasticsearch spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300 spring.data.elasticsearch.repositories.enabled=true3、Notice实体@Data @AllArgsConstructor @NoArgsConstructor //indexName代表所以名称,type代表表名称 @Document(indexName = "wantu_notice_info", type = "doc") public class Notice { //id @JsonProperty("auto_id") private Long id; //标题 @JsonProperty("title") private String title; //公告标签 @JsonProperty("exchange_mc") private String exchangeMc; //公告发布时间 @JsonProperty("create_time") private String originCreateTime; //公告阅读数量 @JsonProperty("read_count") private Integer readCount; }4、NoticeRepository类@Component public interface NoticeRepository extends ElasticsearchRepository<Notice, Long> { }5、NoticeController@RestController @RequestMapping("/api/v1/article") public class NoticeController { @Autowired private NoticeRepository nticeRepository; @GetMapping("save") public CommandResult<Void> save(long id, String title){ Notice article = new Notice(); article.setId(id); article.setReadCount(123); article.setTitle("springboot整合elasticsearch,这个是新版本 2018年"); nticeRepository.save(article); return CommandResult.ofSucceed(); } /** * @param title 搜索标题 * @param pageable page = 第几页参数, value = 每页显示条数 */ @GetMapping("search") public CommandResult<List<Notice>> search(String title,@PageableDefault(page = 1, value = 10) Pageable pageable){ //按标题进行搜索 QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", title); //如果实体和数据的名称对应就会自动封装,pageable分页参数 Iterable<Notice> listIt = nticeRepository.search(queryBuilder,pageable); //Iterable转list List<Notice> list= Lists.newArrayList(listIt); return CommandResult.ofSucceed(list); } }6、查看运行结果它会进行中文分词查询,然后安装相识度进行排序总体步骤还是很清晰简单的,因为有spring-boot-starter-data-elasticsearch进行了整合,所以我们可以少敲很多代码。
一、简介通过两幅图我们可以理解拦截器和过滤器的特点1、过滤器过滤器是在请求进入tomcat容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。理解上面这句话我们就可以知道,进入servlet之前,主要是两个参数:ServletRequest,ServletResponse 那我们得到这两个测试可以干哪些事呢?我们可以通过ServletRequest得到HttpServletRequest,此时你就可以对请求或响应(Request、Response)那就可以对对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、字符集统一等一些高级功能。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。(每次热部署后,都会销毁)。2、拦截器从上图我们可以看出过滤器只在servlet前后起作用,所以它既不能捕获异常,获得bean对象等,这些是只能是进入servlet里面的拦截器能过做到。拦截器中用于在某个方法或字段被访问之前,进行拦截然后,在之前或之后加入某些操作。比如日志,安全等。一般拦截器方法都是通过动态代理的方式实现。可以通过它来进行权限验证,或者判断用户是否登陆,或者是像12306 判断当前时间是否是购票时间。对比一下其实我们可以发现,过滤器能做的事拦截器都能做,二拦截器做的事过滤器不一定做的了。3、监听器listener是servlet规范中定义的一种特殊类。用于监听servletContext、HttpSession和servletRequest等域对象的创建和销毁事件。监听域对象的属性发生修改的事件。用于在事件发生前、发生后做一些必要的处理。其主要可用于以下方面:1、统计在线人数和在线用户2、系统启动时加载初始化信息3、统计网站访问量4、记录用户访问路径。常用的监听器 servletContextListener、httpSessionListener、servletRequestListener)二、如何创建1、过滤器自定义Filter 使用Servlet3.0的注解进行配置第三步的@WebFilter就是3.0的注解1)启动类里面增加 @ServletComponentScan,进行扫描2)新建一个Filter类,implements Filter,并实现对应的接口3)@WebFilter 标记一个类为filter,被spring进行扫描urlPatterns:拦截规则,支持正则4)控制chain.doFilter的方法的调用,来实现是否通过放行不放行,web应用resp.sendRedirect("/index.html");场景:权限控制、用户登录(非前端后端分离场景)等application类@SpringBootApplication @ServletComponentScan public class SpringbootstudyApplication { public static void main(String[] args) { SpringApplication.run(SpringbootstudyApplication.class, args); } }LoginFilter过滤器//过滤器拦截路径 @WebFilter(urlPatterns = "/api/*", filterName = "loginFilter") public class LoginFilter implements Filter{ /** * 容器加载的时候调用 */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("拦截器进入========拦截器进入========"); } /** * 请求被拦截的时候进行调用 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("拦截中========拦截中========"); HttpServletRequest hrequest = (HttpServletRequest)servletRequest; HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) servletResponse); if(hrequest.getRequestURI().indexOf("/index") != -1 || hrequest.getRequestURI().indexOf("/asd") != -1 || hrequest.getRequestURI().indexOf("/online") != -1 || hrequest.getRequestURI().indexOf("/login") != -1 ) { filterChain.doFilter(servletRequest, servletResponse); }else { wrapper.sendRedirect("/login"); } } /** * 容器被销毁的时候被调用 */ @Override public void destroy() { System.out.println("拦截器销毁========拦截器销毁========"); } }1、官网地址:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners2、监听器@WebListener public class RequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { // TODO Auto-generated method stub System.out.println("======销毁监听器========"); } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("======进入监听器========"); }3、拦截器CustomWebMvcConfigurer主拦截器需要:1:添加@Configuration注解 2:实现WebMvcConfigurer接口//主拦截器,根据拦截不同路径跳转不同自定义拦截器 (实现WebMvcConfigurer方法) @Configuration public class CustomWebMvcConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginIntercepter()).addPathPatterns("/api1/*/**"); registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api2/*/**"); //.excludePathPatterns("/api2/xxx/**"); //拦截全部 /*/*/** WebMvcConfigurer.super.addInterceptors(registry); } }LoginIntercepter子拦截器public class LoginIntercepter implements HandlerInterceptor{ /** * 进入controller方法之前 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginIntercepter------->preHandle"); // String token = request.getParameter("access_token"); // // response.getWriter().print("fail"); return HandlerInterceptor.super.preHandle(request, response, handler); } /** * 调用完controller之后,视图渲染之前 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("LoginIntercepter------->postHandle"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } /** * 整个完成之后,通常用于资源清理 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("LoginIntercepter------->afterCompletion"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }TwoIntercepter同上三、总结最后总解一下他们:过滤器:用于属性甄别,对象收集(不可改变过滤对象的属性和行为)监听器:用于对象监听,行为记录(不可改变监听对象的属性和行为)拦截器:用于对象拦截,行为干预(可以改变拦截对象的属性和行为)
一、存储引擎是什么存储引擎是数据库的核心,对于mysql来说,存储引擎是以插件的形式运行的。MySQL默认配置了许多不同的存储引擎,可以预先设置或者在MySQL服务器中启用。你可以选择适用于服务器、数据库和表格的存储引擎,以便在选择如何存储你的信息、如何检索这些信息以及你需要你的数据结合什么性能和功能的时候为你提供最大的灵活性。二、存储引擎有哪些MySQL版本为5以后的(博客测试版本是MySQL5.5.15)1、查看MySQL默认支持的存储引擎show engines \g;可以看到,这里一共有九种默认的存储引擎,但能够经常使用到的其实只有那么几种,2、各主要存储引擎对比三、常用存储引擎介绍1、InnoDBMySQL5.5后的默认数据库存储引擎。这个可能是平常用的最多的一个数据库引擎了,因为他支持事务,MySQL中支持事务的存储引擎并不多,除了InnoDB,还有NDB(而且NDB只支持在NDB集群中使用),所以一般需要用到事务的场景,我们一般选用的就是InnoDB存储引擎。(1)优缺点优点:支持ACID事务;聚集索引;支持外键;支持行锁;存在着缓冲管理,通过缓冲池,将索引和数据全部缓存起来,加快查询的速度;缺点:不支持全文索引,不保存表行数,相比MyISAM,InnoDB写的处理效率要差一些,并且会占用更多的磁盘空间以保留数据和索引。(2)存储方式① 使用共享表空间存储:这种方式创建的表结构保存在.frm文件中,数据和索引保存在innodb_data_home_dir和innodb_data_file_path定义的表空间中,可以是多个文件。② 使用多表空间存储:这种方式创建的表结构仍然保存在.frm文件中,但是每个表的数据和索引单独保存在.idb文件中。如果是个分区表,则每个分区对应单独的.idb文件,文件名是“表名+分区名”,可以在创建分区的时候指定每个分区的数据文件的位置,以此来将表的IO均匀分布在多个磁盘上。要使用多表空间的存储方式,需要设置参数innodb_file_per_table并重启服务器后才可以生效,而且只对新建的表生效。多表空间的数据文件没有大小限制,不需要设置初始大小,也不需要设置文件的最大限制、扩展大小等参数。即使在多表空间的存储方式下,共享表空间仍然是必须的,InnoDB把内部数据词典和工作日志放在这个文件中,所以备份使用多表空间特性的表时直接复制.idb文件是不行的,可以通过命令将数据备份恢复到数据库中:ALTER TABLE tbl_name DISCARD TABLESPACE; ALTER TABLE tbl_name IMPORT TABLESPACE;但是这样只能恢复到表原来所在数据库中,如果需要恢复到其他数据库则需要通过mysqldump和mysqlimport来实现。(3)数据文件InnoDB的数据文件由表的存储方式决定。① 共享表空间文件:由参数innodb_data_home_dir和innodb_data_file_path定义,用于存放数据词典和日志等。② .frm:存放表结构定义,共享表空间存储与多表空间存储均有。③ .idb:使用多表空间存储方式时,用于存放表数据和索引,若使用共享表空间存储则无此文件。(4)外键约束InnoDB是MySQL唯一支持外键约束的引擎。外键约束可以让数据库自己通过外键保证数据的完整性和一致性,但是引入外键会使速度和性能下降。在创建外键的时候,要求父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引。(5)适用情况如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询以外,还包括很多的更新、删除操作,那么InnoDB 存储引擎应该是比较合适的选择。InnoDB 存储引擎除了有效地降低由于删除和更新导致的锁定,还可以确保事务的完整提交和回滚,对于类似计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB 都是合适的选择。2、MyISAM(1)优缺点优点:全文索引,非聚集索引、查询速度快。缺点:不支持事务、不支持外键、仅仅支持表锁;数据库所在主机如果宕机,MyISAM的数据文件容易损坏,而且难恢复;。(2)存储方式① 静态表(默认):字段都是非变长的(每个记录都是固定长度的)。存储非常迅速、容易缓存,出现故障容易恢复;占用空间通常比动态表多。② 动态表:占用的空间相对较少,但是频繁的更新删除记录会产生碎片,需要定期执行optimize table或myisamchk -r命令来改善性能,而且出现故障的时候恢复比较困难。③ 压缩表:使用myisampack工具创建,占用非常小的磁盘空间。因为每个记录是被单独压缩的,所以只有非常小的访问开支。静态表的数据在存储的时候会按照列的宽度定义补足空格,在返回数据给应用之前去掉这些空格。如果需要保存的内容后面本来就有空格,在返回结果的时候也会被去掉。(其实是数据类型char的行为,动态表中若有这个数据类型也同样会有这个问题)(静态表和动态表是根据正使用的列的类型自动选择的。)(3)数据文件MyISAM数据表在磁盘存储成3个文件,其文件名都和表名相同,扩展名分别是:① .frm:存储数据表结构定义。② .MYD:存储表数据。③ .MYI:存储表索引。其中,数据文件和索引文件可以放置在不同的目录,平均分布IO,获得更快的速度。指定索引文件和数据文件的路径,需要在创建表的时候通过data directory和index directory语句指定。(文件路径需要是绝对路径并且具有访问的权限)MyISAM类型的表可能会损坏,原因可能是多种多样的,损坏后的表可能不能访问,会提示需要修复或者访问后返回错误的结果。可以使用check table语句来检查MyISAM表的健康,并用repair table语句修复已经损坏的MyISAM表。frm和MYI可以存放在不同的目录下。MYI文件用来存储索引,但仅保存记录所在页的指针,索引的结构是B+树结构。下面这张图就是MYI文件保存的机制:从这张图可以发现,这个存储引擎通过MYI的B+树结构来查找记录页,再根据记录页查找记录。并且支持全文索引、B树索引和数据压缩。(4)适用情况如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常适合的。MyISAM 是在Web、数据仓库和其他应用环境下最常使用的存储引擎之一。3、MEMORY(1)优缺点优点:①分离静态的和动态的数据,②利用结构接近的数据来优化查询,③查询时可以访问更少的数据,④更容易维护大数据集,⑤可以通过修改.mrg文件来修改merge表,当然也可以用alter进行修改,修改后要通过flush tables刷新表缓存,此法可以动态增加减少子表,⑥Memory存储引擎将表的数据存放在内存中,能像会话或缓存一样方便操作和管理,充分发挥内存引擎的特点——速度快,延迟低;仅仅读或写为主的访问模式。缺点:一旦服务关闭,表中的数据就会丢失;对表的大小有限制。(2)存储方式数据存放在内存中。(3)数据文件每个MEMORY表只对应一个.frm磁盘文件,用于存储表的结构定义,表数据存放在内存中。默认使用HASH索引,而不是BTREE索引。(4)适用情况Memory存储引擎主要用在那些内容变化不频繁的代码表,或者作为统计操作的中间结果表,便于高效地对中间结果进行分析并得到最终的统计结果。4、MRG_MYISAM (MERGE)(1)引擎原理Merge存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,merge表本身并没有数据,对merge类型的表可以进行查询、更新、删除的操作,这些操作实际上是对内部的实际的MyISAM表进行的。通过insert_method子句定义merge表的插入操作:使用first或last可以使插入操作被相应地作用在第一或最后一个表上,不定义或定义为No表示不能对这个merge表进行插入操作。对merge表进行drop操作只是删除了merge的定义,对内部的表没有任何影响。(2)存储方式(3)数据文件① .frm:存储表定义。② .MRG:存储组合表的信息,包括merge表由哪些表组成、插入新数据时的依据。可以通过修改.mrg文件来修改merge表,但是修改后要通过flush tables刷新。(4)适用情况用于将一系列等同的MyISAM 表以逻辑方式组合在一起,并作为一个对象引用它们。MERGE 表的优点在于可以突破对单个MyISAM 表大小的限制,并且通过将不同的表分布在多个磁盘上,可以有效地改善MERGE 表的访问效率。这对于诸如数据仓储等VLDB环境十分适合。5、ARCHIVEArchive存储引擎基本上用于数据归档;它的压缩比非常的高,存储空间大概是InnoDB的10-15分之一所以它用来存储历史数据非常的适合,由于它不支持索引同时也不能缓存索引和数据,所以它不适合作为并发访问表的存储引擎。archive存储引擎使用行锁来实现高并发插入操作,但是它不支持事务,其设计目标只是提供高速的插入和压缩功能。6、BLACKHOLE黑洞引擎,学习过网络的同学大概听过黑洞路由器,在Linux系统中接触过/dev/null,这个引擎和这一样,表不保存数据库,主要作用是如果主被模式slave节点过多,通过blachole减轻master的压力。7、FEDERATED主要是用于和远程数据库链接的引擎8、CSVCSV存储引擎可以将CSV文件作为MySQL的表进行处理。存储格式就是普通的CSV文件特点:①以CSV格式进行数据存储、②所有的列必须都是不能为null的、③不支持索引,不适合大表,不适合在线处理、④可以对数据文件直接编辑9、PERFORMANCE_SCHEMA用于监控MySQL server在一个较低级别的运行过程中的资源消耗、资源等待等情况10、NDB(也称为NDBCLUSTER)-此集群数据库引擎特别适合于需要最高正常运行时间和可用性的应用程序。
一、bootstrap.yml / bootstrap.properties与application.yml / application.properties执行顺序bootstrap.yml(bootstrap.properties)用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。bootstrap.yml 先于 application.yml 加载二、典型的应用场景当使用 Spring Cloud Config Server 的时候,你应该在 bootstrap.yml 里面指定 spring.application.name 和 spring.cloud.config.server.git.uri和一些加密/解密的信息技术上,bootstrap.yml 是被一个父级的 Spring ApplicationContext 加载的。这个父级的 Spring ApplicationContext是先加载的,在加载application.yml 的 ApplicationContext之前。为何需要把 config server 的信息放在 bootstrap.yml 里?当使用 Spring Cloud 的时候,配置信息一般是从 config server 加载的,为了取得配置信息(比如密码等),你需要一些提早的引导配置。因此,把 config server 信息放在 bootstrap.yml,用来加载在这个时期真正需要的配置信息。三、高级使用场景启动上下文Spring Cloud会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文。初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment。Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。 Bootstrap context和Application Context有着不同的约定,所以新增了一个bootstrap.yml文件,而不是使用application.yml (或者application.properties)。保证Bootstrap Context和Application Context配置的分离。下面是一个例子: bootstrap.ymlspring: application: name: foo cloud: config: uri: ${SPRING_CONFIG_URI:http://localhost:8888}推荐在bootstrap.yml or application.yml里面配置spring.application.name. 你可以通过设置spring.cloud.bootstrap.enabled=false来禁用bootstrap。应用上下文层次结构如果你通过SpringApplication或者SpringApplicationBuilder创建一个Application Context,那么会为spring应用的Application Context创建父上下文Bootstrap Context。在Spring里有个特性,子上下文会继承父类的property sources and profiles ,所以main application context 相对于没有使用Spring Cloud Config,会新增额外的property sources。额外的property sources有:“bootstrap” : 如果在Bootstrap Context扫描到PropertySourceLocator并且有属性,则会添加到CompositePropertySource。Spirng Cloud Config就是通过这种方式来添加的属性的,详细看源码ConfigServicePropertySourceLocator`。下面也也有一个例子自定义的例子。“applicationConfig: [classpath:bootstrap.yml]” ,(如果有spring.profiles.active=production则例如 applicationConfig: [classpath:/bootstrap.yml]#production): 如果你使用bootstrap.yml来配置Bootstrap Context,他比application.yml优先级要低。它将添加到子上下文,作为Spring Boot应用程序的一部分。下文有介绍。由于优先级规则,Bootstrap Context不包含从bootstrap.yml来的数据,但是可以用它作为默认设置。你可以很容易的扩展任何你建立的上下文层次,可以使用它提供的接口,或者使用SpringApplicationBuilder包含的方法(parent(),child(),sibling())。Bootstrap Context将是最高级别的父类。扩展的每一个Context都有有自己的bootstrap property source(有可能是空的)。扩展的每一个Context都有不同spring.application.name。同一层层次的父子上下文原则上也有一有不同的名称,因此,也会有不同的Config Server配置。子上下文的属性在相同名字的情况下将覆盖父上下文的属性。注意SpringApplicationBuilder允许共享Environment到所有层次,但是不是默认的。因此,同级的兄弟上下文不在和父类共享一些东西的时候不一定有相同的profiles或者property sources。修改Bootstrap属性配置源码位置BootstrapApplicationListener。 String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}"); Map<String, Object> bootstrapMap = new HashMap<>();bootstrapMap.put("spring.config.name",configName); if(StringUtils.hasText(configLocation)){ bootstrapMap.put("spring.config.location", configLocation); }bootstrap.yml是由spring.cloud.bootstrap.name(默认:”bootstrap”)或者spring.cloud.bootstrap.location(默认空)。这些属性行为与spring.config.*类似,通过它的Environment来配置引导ApplicationContext。如果有一个激活的profile(来源于spring.profiles.active或者Environment的Api构建),例如bootstrap-development.properties 就是配置了profile为development的配置文件.覆盖远程属性property sources被bootstrap context 添加到应用通常通过远程的方式,比如”Config Server”。默认情况下,本地的配置文件不能覆盖远程配置,但是可以通过启动命令行参数来覆盖远程配置。如果需要本地文件覆盖远程文件,需要在远程配置文件里设置授权spring.cloud.config.allowOverride=true(这个配置不能在本地被设置)。一旦设置了这个权限,你可以配置更加细粒度的配置来配置覆盖的方式,比如:spring.cloud.config.overrideNone=true 覆盖任何本地属性spring.cloud.config.overrideSystemProperties=false 仅仅系统属性和环境变量源文件见PropertySourceBootstrapProperties自定义启动配置bootstrap context是依赖/META-INF/spring.factories文件里面的org.springframework.cloud.bootstrap.BootstrapConfiguration条目下面,通过逗号分隔的Spring @Configuration类来建立的配置。任何main application context需要的自动注入的Bean可以在这里通过这种方式来获取。这也是ApplicationContextInitializer建立@Bean的方式。可以通过@Order来更改初始化序列,默认是”last”。# spring-cloud-context-1.1.1.RELEASE.jar # spring.factories # AutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\ org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ org.springframework.cloud.context.restart.RestartListener # Bootstrap components org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\ org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration警告小心,你添加的自定义BootstrapConfiguration类没有错误的@ComponentScanned到你的主应用上下文,他们可能是不需要的。使用一个另外的包不被@ComponentScan或者@SpringBootApplication注解覆盖到。bootstrap context通过spring.factories配置的类初始化的所有的Bean都会在SpingApplicatin启动前加入到它的上下文里去。自定义引导配置来源:Bootstrap Property Sources默认的property source添加额外的配置是通过配置服务(Config Server),你也可以自定义添加property source通过实现PropertySourceLocator接口来添加。你可以使用它加配置属性从不同的服务、数据库、或者其他。下面是一个自定义的例子:@Configuration public class CustomPropertySourceLocator implements PropertySourceLocator { @Override public PropertySource<?> locate(Environment environment) { return new MapPropertySource("customProperty", Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended")); } }Environment被ApplicationContext建立,并传入property sources(可能不同个profile有不同的属性),所以,你可以从Environment寻找找一些特别的属性。比如spring.application.name,它是默认的Config Server property source。如果你建立了一个jar包,里面添加了一个META-INF/spring.factories文件:org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator那么,”customProperty“的PropertySource将会被包含到应用。
一、说明本文介绍基于 Kubernetes(k8s) 环境集成阿里云 私有镜像仓库 来部署一套 Dubbo + Nacos 的微服务系统,并使用 Kubernetes DNS 以及 port-forward 的方式来打通网络访问。二、部署 MySQL创建配置文件 mysql-local.yaml 内容如下:apiVersion: v1 kind: ReplicationController metadata: name: mysql labels: name: mysql spec: replicas: 1 selector: name: mysql template: metadata: labels: name: mysql spec: containers: - name: mysql image: nacos/nacos-mysql:5.7 ports: - containerPort: 3306 volumeMounts: - name: mysql-data mountPath: /var/lib/mysql env: - name: MYSQL_ROOT_PASSWORD value: "root" - name: MYSQL_DATABASE value: "nacos_devtest" - name: MYSQL_USER value: "nacos" - name: MYSQL_PASSWORD value: "nacos" volumes: - name: mysql-data hostPath: path: /var/lib/mysql --- apiVersion: v1 kind: Service metadata: name: mysql labels: name: mysql spec: ports: - port: 3306 targetPort: 3306 selector: name: mysqlReplicationController 简称 RC 可以保证在任意时间运行 Pod 的副本数量,能够保证 Pod 总是可用的。执行以下命令,部署 MySQL 5.7:kubectl apply -f mysql-local.yaml三、部署 Nacos创建配置文件 nacos-standalone-start.yaml 内容如下:--- apiVersion: v1 kind: Service metadata: name: nacos-standalone labels: app: nacos-standalone spec: type: ClusterIP clusterIP: None ports: - port: 8848 name: server targetPort: 8848 - port: 9848 name: client-rpc targetPort: 9848 - port: 9849 name: raft-rpc targetPort: 9849 ## 兼容1.4.x版本的选举端口 - port: 7848 name: old-raft-rpc targetPort: 7848 selector: app: nacos --- apiVersion: v1 kind: ConfigMap metadata: name: nacos-cm data: mysql.host: "mysql" mysql.db.name: "nacos_devtest" mysql.port: "3306" mysql.user: "nacos" mysql.password: "nacos" --- apiVersion: apps/v1 kind: Deployment metadata: name: nacos spec: replicas: 1 template: metadata: labels: app: nacos annotations: pod.alpha.kubernetes.io/initialized: "true" spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - nacos topologyKey: "kubernetes.io/hostname" containers: - name: nacos imagePullPolicy: Always image: nacos/nacos-server:latest resources: requests: memory: "1Gi" cpu: "500m" ports: - containerPort: 8848 name: client - containerPort: 9848 name: client-rpc - containerPort: 9849 name: raft-rpc - containerPort: 7848 name: old-raft-rpc env: - name: SPRING_DATASOURCE_PLATFORM value: "mysql" - name: MYSQL_SERVICE_HOST valueFrom: configMapKeyRef: name: nacos-cm key: mysql.host - name: MYSQL_SERVICE_DB_NAME valueFrom: configMapKeyRef: name: nacos-cm key: mysql.db.name - name: MYSQL_SERVICE_PORT valueFrom: configMapKeyRef: name: nacos-cm key: mysql.port - name: MYSQL_SERVICE_USER valueFrom: configMapKeyRef: name: nacos-cm key: mysql.user - name: MYSQL_SERVICE_PASSWORD valueFrom: configMapKeyRef: name: nacos-cm key: mysql.password - name: MODE value: "standalone" - name: NACOS_SERVER_PORT value: "8848" - name: PREFER_HOST_MODE value: "hostname" selector: matchLabels: app: nacos使用 ConfigMap 对象来配置 MySQL 的参数;Nacos 通过 DNS 来访问数据库的 Service。执行以下命令,部署 Nacos 最新版本:kubectl apply -f nacos-standalone-start.yaml执行以下命令,暴露 Nacos 的端口到宿主机中给外部访问:nohup kubectl port-forward svc/nacos-standalone 8848:8848 9848:9848 9849:9849 7848:7848 --addreskubectl port-forward 通过端口转发映射本地端口到指定的应用端口,它将在您的计算机和 kubernetes 之间创建一条隧道;一般用于测试、实验室、故障排除,而不是长期的解决方案。四、部署 Dubbo 服务4.1. 创建镜像仓库的密钥由于拉取阿里云仓库的私有镜像时需要输入账户和密码,需要用到 k8s 的 Secret 对象来管理密码秘钥等敏感信息。执行以下命令,创建 Secret 对象:kubectl create secret docker-registry aliyuncs \ --docker-server=registry.cn-guangzhou.aliyuncs.com \ --docker-username=zltdiablo@163.com \ --docker-password=xxxxxxocker-registry 指定 secret 的名称docker-server 仓库地址docker-username 仓库账号docker-password 仓库密码创建成功后,可以使用以下命令查看密钥信息:kubectl get secret aliyuncs --output=yaml4.2. 部署 provider 服务创建配置文件 provider.yaml 内容如下:--- apiVersion: v1 kind: Service metadata: name: zlt-nacos-provider spec: clusterIP: None selector: app: zlt-nacos-provider ports: - protocol: TCP port: 20880 targetPort: 20880 --- apiVersion: apps/v1 kind: Deployment metadata: name: zlt-nacos-provider spec: replicas: 1 selector: matchLabels: app: zlt-nacos-provider template: metadata: labels: app: zlt-nacos-provider spec: imagePullSecrets: - name: aliyuncs containers: - name: server image: registry.cn-guangzhou.aliyuncs.com/zlt-test/nacos-provider:1.0-SNAPSHOT imagePullPolicy: IfNotPresent ports: - containerPort: 20880 env: - name: DUBBO_REGISTRY_ADDRESS value: "nacos://nacos-standalone:8848" - name: DUBBO_IP_TO_REGISTRY value: "zlt-nacos-provider"DUBBO_REGISTRY_ADDRESS 参数指定注册中心地址,使用 DNS 来访问 NacosDUBBO_IP_TO_REGISTRY 参数指定服务注册的 IP 地址,配置自己 Service 的名称通过 imagePullSecrets 参数来绑定登录镜像仓库所使用的 secret 名称。执行以下命令,部署 provider 最新版本:kubectl apply -f provider.yaml4.3. 部署 consumer 服务创建配置文件 consumer.yaml 内容如下:--- apiVersion: v1 kind: Service metadata: name: zlt-nacos-consumer spec: clusterIP: None selector: app: zlt-nacos-consumer ports: - name: web port: 8080 targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: zlt-nacos-consumer spec: replicas: 1 selector: matchLabels: app: zlt-nacos-consumer template: metadata: labels: app: zlt-nacos-consumer spec: imagePullSecrets: - name: aliyuncs containers: - name: server image: registry.cn-guangzhou.aliyuncs.com/zlt-test/nacos-consumer:1.0-SNAPSHOT imagePullPolicy: IfNotPresent ports: - containerPort: 8080 env: - name: DUBBO_REGISTRY_ADDRESS value: "nacos://nacos-standalone:8848" - name: DUBBO_IP_TO_REGISTRY value: "zlt-nacos-consumer"执行以下命令,部署 consumer 最新版本:kubectl apply -f consumer.yaml五、测试通过命令 kubectl get pod 查看所有创建的 pods 确保所有的状态都为 Running执行以下命令,暴露 consumer 服务的 web 端口到宿主机中给外部访问:nohup kubectl port-forward svc/zlt-nacos-consumer 8080:8080 --address='0.0.0.0' &在浏览器输入以下地址进行访问:http://宿主机IP:8080/test?name=123
微软 Bing 在线 AI 绘图功能上线,由 Open AI 的 DALL-E 驱动,可通过文字描述生成图片内容。Bing 绘图目前仅支持英文,使用“形容词 + 名词 + 动词 + 风格”的格式可以生成高质量图片。我想要一张图片:大象、珊瑚、火山、云朵I want a picture with: elephants, coral, volcanoes, clouds我想要一张图片:亚特兰蒂斯,奥利匹克,喜马拉雅山I want a picture with: Atlantis, Olympics, Himalayas我想要一张图片:洗衣机、长颈鹿、电视、鲸鱼I want a picture with: washing machine, giraffe, TV, whale我想要一张蓝色长颈鹿、红色鲸鱼和飘逸的绿色长发的图片I want a picture with blue giraffes, red whales, and long flowing green hair我想要一张有趣的Docker标志的图片I want a picture with an interesting docker logo我想要一张图片:五彩斑斓的黑I want a picture with: colorful black我想要一张图片:要有风,要有肉;要有火锅,要有雾;要有美女,要有驴!I want a picture in which there should be wind and meat; To have hot pot, there should be fog; To have beautiful women, to have donkeys!
一、SpringSpring 是一个轻量级的Java 开发框架,主要依存于SSM 框架,即Spring MVC + Spring + Mybatis,定位很明确,Spring MVC主要负责view 层的显示,Spring 利用IOC 和AOP 来处理业务,Mybatis则是数据的持久化到数据库。而Spring 为了开发Java 应用程序提供了全面的基础架构支持。 它具有以下几个特点。非侵入式所谓非侵入式是指Spring 框架的API 不会出现在业务逻辑上,也就是说业务逻辑应该是干净的,不会出现与业务逻辑无关的程序。 针对应用而言,这样才能将业务逻辑从目前应用中剥离出来,进一步在其他的应用中实现重用; 针对框架而言,由于业务逻辑中没有Spring 的API,所以业务逻辑也可以从Spring 框架快速移植到其他框架。容器Spring 提供了容器功能,容器可以管理对象的生命周期以及对象与对象之间的依赖关系,可以写一个配置文件定义对象的名称、是否单例以及设定与其他物件的依赖关系,当容器启动后,这些物件就被建立好实体了,可以直接注入使用。IOC 控制反转控制反转是一种编程的方式,即依赖关系的转移,如果以前都是依赖于实现,那么现在反转为依赖于抽象,核心思想就在于面对界面编程。依赖注入对象与对象之间依赖关系的实现,包括接口注入,建构注入等。AOP 剖面编程将记录文件、安全、交易管理等功能想象成一个剖面,原先这些功能都是直接写在业务逻辑中,但它有两个缺点,首先就是业务逻辑不干净,其次就是这些功能被很多业务逻辑重复使用,不能做到重用。 AOP 就解决了上述问题,可以把这些功能从业务逻辑中拨离出来形成一个剖面,可以实现重用。二、Spring Boot设计目的Spring Boot 的设计目的是为了解决 Spring 各版本设定工作过于繁重的问题,简化初始架设流程、降低开发难度,使开发人员只需要专注在应用程序的开发,而无须过多关注 XML 的配置。设计理念Spring Boot 的设计理念为约定大于配置(Convention Over Configuration),为此提供了很多已经整合好的依赖套件,以便开发人员在开发应用程序时能做到不用或者很少的Spring 配置就可以让项目快速执行,这样的好处就是开发人员可以把更多的精力放在业务逻辑的开发上。优点使用简单Spring Boot 可使用注释的方式实现类别的定义与功能的开发,即可无程序产生和 XML 设置。 而且Spring Boot 会根据类别路径中的JAR 包,为JAR 包中的类别自动配置Bean,这样可以大量减少我们要使用的配置,当然Spring Boot 只考虑大部分开发状况,若实际开发中我们需要配置Bean 也可以自定义配置。配置简单Spring Boot 为了简化Maven 配置设定提供了大量的Starter,开发人员只要能配置好相对应组件参数,Spring Boot 就会自动配置,让开发人员能快速搭建依赖于Spring 组件的Java 项目,例如,Spring Boot 为了帮助开发人员快速启动一个web 容器, 提供了spring-boot-starter-web 依赖,只需要在pom.xml 档案中新增web 依赖即可。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>而点选进入web 依赖后可以发现,spring-boot-starter-web 已经包含了多个依赖,包括在Spring 中需要汇入的依赖,如下图展示一部分依赖。<!-- 忽略其他依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.0.7.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.7.RELEASE</version> <scope>compile</scope> </dependency>部署简单Spring Boot 内嵌了各种Servlet 容器、Tomcat、Jetty 等,不再需要打包成war 包部署到容器中,我们只需要将项目打包成Jar 包,所有的依赖包都在一个Jar 包内,使用java -jar xxx.jar 即可启动项目。监控简单Spring Boot 提供了spring-boot-starter-actuator 依赖,它是一套监控、管理应用程序状态的功能模块,包含监控应用程序的线程信息、内存信息等。缺点虽然Spring Boot 上手很容易,大部分东西都自动配置,简化了许多架设流程,但也因此需要非常了解Spring Boot 的核心技术原理,需要对配置信息非常熟悉,不然一旦遇到问题就很棘手,要找问题也不是很容易。三、Spring CloudSpring Cloud 是基于 Spring Boot 的一整套实现微服务的框架,它是一套分布式服务治理架构,本身不提供实际功能性的操作,只专注于服务之间的通讯、熔断和监控等,因此需要很多原件来共同支持一套功能。四、三者的关系从上述介绍可以看出,Spring Boot 其实是依赖于Spring 的,并不是一个全新的架构,Spring Boot 就是 Spring 的自动化,而 Spring Cloud 通过依赖 Spring Boot 来置微服务应用。
一、什么是机器学习?机器学习是一种基于数据的人工智能技术,通过对大量数据进行分析和学习,让计算机能够自动进行决策或预测。 机器学习的基本思想是从过去的经验中学习,通过对大量样本的分析,从中发现规律和模式,并且将这些知识应用于未来的预测和决策中。机器学习技术可以分为监督式学习、非监督式学习和强化学习。 在监督式学习中,算法通过对已知的数据进行标记,从而学习出一个可以对未知数据进行预测的模型。 在非监督式学习中,算法通过对数据进行分类、聚类等操作,从而发现数据中的隐藏结构和模式。 在强化学习中,算法通过与环境的交互,从环境中学习如何选择最优的行动,以实现某个目标。机器学习技术已经广泛应用于许多领域,如图像识别、语音识别、自然语言处理、推荐系统、金融风险控制等。 随着数据量的不断增加和计算能力的提升,机器学习技术的应用前景仍然十分广阔。二、ChatGPT 的运作原理ChatGPT是一个基于机器学习技术的自然语言处理模型,它的运作原理是通过大量的文本数据训练出一个深度神经网络模型,从而实现自然语言的生成和理解。 具体来说,当用户输入一段文本时,ChatGPT会通过自然语言处理算法将其转化为数据形式,然后通过模型进行计算和学习,最终生成相应的回答。其运作原理主要包括下列几个方面:预训练ChatGPT 是一个预训练的模型,它的核心是使用海量的文本数据进行无监督的预训练。 这些文本数据包含了各种不同主题和形式的语言,例如维基百科、网页文本、小说等。 透过这些数据的学习,可以让 ChatGPT 学习到自然语言的结构和规则,从而提高对语言的理解和生成能力。转移学习ChatGPT 使用转移学习的方法,将预训练模型转移到不同的任务中,从而进一步提高其在特定任务上的表现。 例如,当需要进行问答任务时, ChatGPT 可以通过微调预训练模型,让其更好地理解问题和生成答案。Transformer 模型ChatGPT 的核心是 Transformer 模型,它是一种基于自注意力机制的深度学习模型,可以对长文本进行高效的处理和理解。 Transformer 模型主要包含了 Encoder 和 Decoder 两部分,Encoder 用于将输入文本转化为特征矢量, Decoder 用于从特征矢量中生成输出文本。Beam Search 算法ChatGPT 在生成文本時使用了 Beam Search 算法,該算法可以生成多個候選答案,從中選擇概率最高的答案。透過 Beam Search 算法, ChatGPT 可以生成更加流暢、自然的文本,提高了其生成文本的質量。三、ChatGPT 生活利用ChatGPT 的应用非常广泛,可以用于自然语言处理、翻译、问答、智能客服等领域。 例如,在智能客服领域中,ChatGPT可以通过对话模拟和分析,帮助企业更好地了解用户需求,提供更好的产品和服务。1、自然语言处理自然语言处理是指计算机通过对人类语言的理解和生成,实现自动处理和分析文本的技术。 ChatGPT 作为一个自然语言处理技术,可以理解和生成自然语言,并通过对语言的结构和含义进行分析,提高文本处理和分析的准确度。2、翻译在翻译方面,ChatGPT可以通过分析不同语言之间的语言结构和表达方式,将一种语言翻译成另一种语言,提高翻译质量和准确度。3、自动回复而在自动回复方面, ChatGPT 可以自动产生回答,提高问答系统的效率和准确度。四、ChatGPT vs 其他相关技术与其他类似技术相比, ChatGPT 的区别在于它是一个基于深度学习的模型,通过大量的文本数据训练出来,具有更好的自主学习能力和适应能力。 ChatGPT在自然语言生成方面表现出色,可以生成具有情感和人性化的回答,进一步提高了用户体验。并且,ChatGPT针对有害内容(例如性、宗教、政治、种族等)议题,都做到了良好的过滤机制,这点是许多其他 AI 模型无法达到的。五、ChatGPT 的未来1、未来发展ChatGPT的出现将对未来产生重要的影响。 上文亦提到它可以应用于多个领域,进一步提高了机器学习技术的应用价值和广泛性。 ChatGPT 的出现也意味着未来人类和机器之间的交互将更加自然和流畅,将改变人们的生活方式和工作方式。2、职业取代AI 的发展,让一个议题渐渐浮出台面,那就是「某种职业是否会被 AI 取代?」 ,这边和各位介绍几种在 ChatGPT 的发展下,最有可能被 AI 取代的职业:3、客服人员ChatGPT 可以替代人工客服,自动回答客户的问题。 尤其是对于一些重复性高、标准答案明确的问题,ChatGPT的效率和精确度都远高于人工客服。4、翻译人员ChatGPT 可以实现自然语言翻译,未来可能会取代一些翻译人员的工作。 尤其是对于一些日常性、常规性的翻译工作, ChatGPT 的效率和精确度也远高于人工翻译。5、语言学家ChatGPT 可以通过学习大量的语言数据,自动理解和生成自然语言,未来可能会在某些程度上取代一些语言学家的工作。随着 ChatGPT 技术的不断发展和完善,它将可能取代一些需要进行自然语言处理的职业,但反面来说,同时也将创造出更多的职业机会和发展空间。6、机遇与挑战机器学习技术的发展为人们带来了许多机遇,例如可以通过自动化减少重复性工作、提高工作效率等。 同时,这些技术也带来了挑战,例如用户数据的隐私保护、人工智能监管等问题需要得到重视和解决。ChatGPT 曾经传出为了让语言模型能够更明确的辨识敏感内容,聘用低薪员工来进行人工判读、训练,这导致了伦理相关的争议。 后续若是ChatGPT或有其他语言模型的发展,都应该慎重考虑这一点的问题。六、结语这篇文章和各位介绍了机器学习、 ChatGPT 的原理,以及对于现在和未来的相关内容介绍。 笔者在ChatGPT推出前,也甚少接触甚至探讨人工智能的领域,写这篇文章时,也常常遇到不理解的内容,查阅了不少资料才写出这篇文章。期望这篇文章能够让大家更加认识机器学习、 ChatGPT 的技术与未来,感谢您的阅读!
容器 bbox01 的 ip addr 信息如下:宿主机的 ip addr 信息如下:通过以上测试可以发现,Docker 守护进程只创建了一对对等虚拟设备接口用于连接 bbox01 容器和宿主机,而 bbox04 容器则直接使用了 bbox01 容器的网卡信息。 这个时候如果将 bbox01 容器停止,会发现 bbox04 容器就只剩下 lo 接口了。然后 bbox01 容器重启以后,bbox04 容器也重启一下,就又可以获取到网卡信息了。5、linkdocker run --link 可以用来链接两个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量。 这种方式官方已不推荐使用,并且在未来版本可能会被移除,所以这里不作为重点讲解,感兴趣可自行了解。 官网警告信息:https://docs.docker.com/network/links/二、自定义网络虽然 Docker 提供的默认网络使用比较简单,但是为了保证各容器中应用的安全性,在实际开发中更推荐使用自定义的网络进行容器管理,以及启用容器名称到 IP 地址的自动 DNS 解析。 从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 --name 为容器命名即可。 但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。1、创建网络通过 docker network create 命令可以创建自定义网络模式,命令提示如下:进一步查看 docker network create 命令使用详情,发现可以通过 --driver 指定网络模式且默认是 bridge 网络模式,提示如下:创建一个基于 bridge 网络模式的自定义网络模式 custom_network,完整命令如下:docker network create custom_network通过 docker network ls 查看网络模式:[root@localhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE b3634bbd8943 bridge bridge local 062082493d3a custom_network bridge local 885da101da7d host host local f4f1b3cf1b7f none null local通过自定义网络模式 custom_network 创建容器:docker run -di --name bbox05 --net custom_network busybox通过 docker inspect 容器名称|ID 查看容器的网络信息,在 NetworkSettings 节点中可以看到详细信息。2、连接网络通过 docker network connect 网络名称 容器名称 为容器连接新的网络模式。docker network connect bridge bbox05通过 docker inspect 容器名称|ID 再次查看容器的网络信息,多增加了默认的 bridge。3、断开网络通过 docker network disconnect 网络名称 容器名称 命令断开网络。docker network disconnect custom_network bbox05通过 docker inspect 容器名称|ID 再次查看容器的网络信息,发现只剩下默认的 bridge。4、移除网络可以通过 docker network rm 网络名称 命令移除自定义网络模式,网络模式移除成功会返回网络模式名称。docker network rm custom_network注意:如果通过某个自定义网络模式创建了容器,则该网络模式无法删除。三、容器间网络通信接下来我们通过所学的知识实现容器间的网络通信。首先明确一点,容器之间要互相通信,必须要有属于同一个网络的网卡。 我们先创建两个基于默认的 bridge 网络模式的容器。docker run -di --name default_bbox01 busyboxdocker run -di --name default_bbox02 busybox通过 docker network inspect bridge 查看两容器的具体 IP 信息。然后测试两容器间是否可以进行网络通信。经过测试,从结果得知两个属于同一个网络的容器是可以进行网络通信的,但是 IP 地址可能是不固定的,有被更改的情况发生,那容器内所有通信的 IP 地址也需要进行更改,能否使用容器名称进行网络通信?继续测试。经过测试,从结果得知使用容器进行网络通信是不行的,那怎么实现这个功能呢? 从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 --name 为容器命名即可。 但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。 我们先基于 bridge 网络模式创建自定义网络 custom_network,然后创建两个基于自定义网络模式的容器。docker run -di --name custom_bbox01 --net custom_network busybox docker run -di --name custom_bbox02 --net custom_network busybox通过 docker network inspect custom_network 查看两容器的具体 IP 信息。然后测试两容器间是否可以进行网络通信,分别使用具体 IP 和容器名称进行网络通信。经过测试,从结果得知两个属于同一个自定义网络的容器是可以进行网络通信的,并且可以使用容器名称进行网络通信。 那如果此时我希望 bridge 网络下的容器可以和 custom_network 网络下的容器进行网络又该如何操作?其实答案也非常简单:让 bridge 网络下的容器连接至新的 custom_network 网络即可。docker network connect custom_network default_bbox01学完容器网络通信,大家就可以练习使用多个容器完成常见应用集群的部署了。
当项目大规模使用 Docker 时,容器通信的问题也就产生了。要解决容器通信问题,必须先了解很多关于网络的知识。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,也有着很多不完善的地方,网络方面就是 Docker 比较薄弱的部分。因此,我们有必要深入了解 Docker 的网络知识,以满足更高的网络需求。一、默认网络安装 Docker 以后,会默认创建三种网络,可以通过 docker network ls 查看。[root@localhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 688d1970f72e bridge bridge local 885da101da7d host host local f4f1b3cf1b7f none null local在学习 Docker 网络之前,我们有必要先来了解一下这几种网络模式都是什么意思。1、bridge 网络模式在该模式中,Docker 守护进程创建了一个虚拟以太网桥 docker0,新建的容器会自动桥接到这个接口,附加在其上的任何网卡之间都能自动转发数据包。 默认情况下,守护进程会创建一对对等虚拟设备接口 veth pair,将其中一个接口设置为容器的 eth0 接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx 这样的名字命名,从而将宿主机上的所有容器都连接到这个内部网络上。 比如我运行一个基于 busybox 镜像构建的容器 bbox01,查看 ip addr:busybox 被称为嵌入式 Linux 的瑞士军刀,整合了很多小的 unix 下的通用功能到一个小的可执行文件中。然后宿主机通过 ip addr 查看信息如下:通过以上的比较可以发现,证实了之前所说的:守护进程会创建一对对等虚拟设备接口 veth pair,将其中一个接口设置为容器的 eth0 接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx 这样的名字命名。 同时,守护进程还会从网桥 docker0 的私有地址空间中分配一个 IP 地址和子网给该容器,并设置 docker0 的 IP 地址为容器的默认网关。也可以安装 yum install -y bridge-utils 以后,通过 brctl show 命令查看网桥信息。、对于每个容器的 IP 地址和 Gateway 信息,我们可以通过 docker inspect 容器名称|ID 进行查看,在 NetworkSettings 节点中可以看到详细信息。我们可以通过 docker network inspect bridge 查看所有 bridge 网络模式下的容器,在 Containers 节点中可以看到容器名称。关于 bridge 网络模式的使用,只需要在创建容器时通过参数 --net bridge 或者 --network bridge 指定即可,当然这也是创建容器默认使用的网络模式,也就是说这个参数是可以省略的。Bridge 桥接模式的实现步骤主要如下:Docker Daemon 利用 veth pair 技术,在宿主机上创建一对对等虚拟网络接口设备,假设为 veth0 和 veth1。而veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方。Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0 网桥上。保证宿主机的网络报文可以发往 veth0;Docker Daemon 将 veth1 添加到 Docker Container 所属的 namespace 下,并被改名为 eth0。如此一来,宿主机的网络报文若发往 veth0,则立即会被 Container 的 eth0 接收,实现宿主机到 Docker Container 网络的联通性;同时,也保证 Docker Container 单独使用 eth0,实现容器网络环境的隔离性。2、host 网络模式host 网络模式需要在创建容器时通过参数 --net host 或者 --network host 指定;采用 host 网络模式的 Docker Container,可以直接使用宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换;host 网络模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。比如我基于 host 网络模式创建了一个基于 busybox 镜像构建的容器 bbox02,查看 ip addr:然后宿主机通过 ip addr 查看信息如下:对,你没有看错,返回信息一模一样,我也可以肯定我没有截错图,不信接着往下看。我们可以通过 docker network inspect host 查看所有 host 网络模式下的容器,在 Containers 节点中可以看到容器名称。3、none 网络模式none 网络模式是指禁用网络功能,只有 lo 接口 local 的简写,代表 127.0.0.1,即 localhost 本地环回接口。在创建容器时通过参数 --net none 或者 --network none 指定;none 网络模式即不为 Docker Container 创建任何的网络环境,容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。可以说 none 模式为 Docke Container 做了极少的网络设定,但是俗话说得好“少即是多”,在没有网络配置的情况下,作为 Docker 开发者,才能在这基础做其他无限多可能的网络定制开发。这也恰巧体现了 Docker 设计理念的开放。比如我基于 none 网络模式创建了一个基于 busybox 镜像构建的容器 bbox03,查看 ip addr:我们可以通过 docker network inspect none 查看所有 none 网络模式下的容器,在 Containers 节点中可以看到容器名称。4、container 网络模式Container 网络模式是 Docker 中一种较为特别的网络的模式。在创建容器时通过参数 --net container:已运行的容器名称|ID 或者 --network container:已运行的容器名称|ID 指定;处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用 localhost 高效快速通信。Container 网络模式即新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样两个容器除了网络方面相同之外,其他的如文件系统、进程列表等还是隔离的。 比如我基于容器 bbox01 创建了 container 网络模式的容器 bbox04,查看 ip addr:
一、Spring Boot 的特性Spring Boot 拥有一系列独特的特性,下面我们将对其中的几个进行详细介绍。1、自动配置Spring Boot 的自动配置功能让开发人员无需手动配置 Spring 应用程序,减少了配置的时间和工作量。Spring Boot 会自动根据应用程序所需的配置信息进行配置,包括数据源、缓存等。2、起步依赖Spring Boot 提供了大量的起步依赖,这些依赖包含了大量的类库和框架,可以满足开发人员的需求。开发人员可以根据项目需要选择需要的依赖,以及自定义依赖。3、命令行界面Spring Boot 提供了命令行界面,可以在命令行上运行 Spring Boot 应用程序。这样可以方便地对应用程序进行测试和调试。4、ActuatorSpring Boot 的 Actuator 提供了一系列监控和管理功能,包括健康检查、配置信息、日志等。开发人员可以通过 Actuator 来监控和管理 Spring Boot 应用程序。5、简化的配置Spring Boot 简化了应用程序的配置过程,开发人员只需提供应用程序所需的信息,即可完成应用程序的配置。例如,开发人员只需提供数据源的连接信息,即可完成数据库的配置。二、Spring Boot 的优势Spring Boot 的优势主要表现在以下几个方面:1、简化配置Spring Boot 简化了应用程序的配置过程,减少了配置的时间和工作量。开发人员可以将更多的精力投入到应用程序的开发和测试上,提高开发效率。2、快速开发Spring Boot 提供了大量的起步依赖,可以快速地搭建应用程序。开发人员可以根据需要选择需要的依赖,以及自定义依赖,快速搭建应用程序。3、微服务架构Spring Boot 的特性适合微服务架构的开发。开发人员可以使用 Spring Boot 来构建微服务应用程序,快速部署应用程序。4、高度可扩展Spring Boot 高度可扩展,开发人员可以根据项目需要选择所需的依赖,同时还可以自定义依赖。这种高度可扩展的特性可以帮助开发人员构建更加灵活的应用程序。5、高度集成Spring Boot 高度集成了其他 Spring 组件,如 Spring Data、Spring Security、Spring MVC 等,开发人员可以轻松地使用这些组件来构建应用程序。6、简化测试Spring Boot 简化了应用程序的测试过程。开发人员可以通过命令行界面在命令行上运行 Spring Boot 应用程序,并进行测试和调试。三、Spring Boot 的使用方法使用 Spring Boot 开发应用程序需要掌握以下几个步骤:1、配置环境首先需要配置好 Java 开发环境,包括 JDK、Maven 等。2、创建 Spring Boot 项目可以使用 Spring Initializr 来创建 Spring Boot 项目。Spring Initializr 是一个基于 Web 的项目生成器,可以快速地生成 Spring Boot 项目。3、添加依赖根据项目需要选择所需的依赖,可以在 pom.xml 文件中添加依赖,也可以使用命令行界面添加依赖。4、编写代码根据项目需求编写代码,使用 Spring Boot 的注解和组件来构建应用程序。5、测试应用程序可以使用命令行界面在命令行上运行 Spring Boot 应用程序,并进行测试和调试。6、打包部署可以使用 Maven 打包应用程序,生成 jar 或 war 包。然后将包部署到服务器上即可。四、入门案例要使用Spring Boot,我们只需要添加spring-boot-starter-parent作为父项目,并添加相应的启动器作为依赖。例如,如果我们想要创建一个Web应用,我们可以添加spring-boot-starter-web作为依赖。然后,我们只需要创建一个带有@SpringBootApplication注解的主类,并在其中调用SpringApplication.run方法即可启动应用。import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }接下来,我们可以创建一个带有@RestController注解的控制器类,并使用@GetMapping注解映射请求路径和方法。当从浏览器或命令行发送请求时,该方法会返回纯文本数据。import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/") public String index() { return "Hello, Spring Boot!"; } }这样,我们就完成了一个简单的Web应用。我们可以使用mvn spring-boot:run命令运行该应用,并在浏览器中访问http://localhost:8080/查看结果。五、结语Spring Boot 是一款非常优秀的开源应用程序开发工具,它简化了应用程序的配置和开发过程,提高了开发效率。Spring Boot 的特性适合微服务架构的开发,可以帮助开发人员构建更加灵活、高度可扩展的应用程序。同时,Spring Boot 高度集成了其他 Spring 组件,如 Spring Data、Spring Security、Spring MVC 等,可以帮助开发人员更加方便地构建应用程序。因此,学习和掌握 Spring Boot 是非常有必要的。
目前国内主流的低代码开发平台有:宜搭、简道云、明道云、云程、氚云、伙伴云、道一云、JEPaaS、华炎魔方、搭搭云、JeecgBoot 、RuoYi等。这些平台各有优劣势,定位也不同,用户可以根据自己需求选择。一、阿里云宜搭宜搭是阿里巴巴集团在2019年3月公测的面向业务开发者的零代码业务应用搭建平台。开发者可以通过可视化开发单据页面、流程页面、报表页面、展示页面、外部链接页面,将这些页面组合在一起形成轻应用,一键发布到PC和手机端。宜搭在2018年在阿里巴巴集团内部发布,阿里作为首个种子用户,到目前为止已有上万个应用在上面使用。 在2019年9月宜搭也发布了升级版宜搭Plus,在单据、流程、报表等方面都进行了全面升级,成为一款面向ISV开发者的低代码开发平台,能够满足企业复杂业务管理系统开发所需的数据建模、逻辑&服务编排、专业UI页面设计等需求。优势:大厂产品,用户体验较好,开发简单应用较为便捷,定位阿里生态,发展较快。劣势:只能线上用,不能私有化部署,不交付源代码,价格较高。二、简道云简道云发布至今快五年了,已经算是市面上发展比较久的产品了,它由国内专业的大数据BI和分析平台厂商——南京帆软软件有限公司(以下简称“帆软”)孵化而来,在数据分析方面具有天然的优势。 简道云与帆软旗下报表、BI等其他产品定位有所不同,简道云致力于帮助非技术人员快速搭建企业应用,无需使用编码,即可搭建销售、OA办公、生产等管理应用,帮助企业规范业务流程、促进团队协作、实现数据追踪!简道云的核心功能包括表单、流程表单、仪表盘以及其他一些高级功能,通过这些功能的组合,搭建出各种不同的应用。优势:强大数据分析功能、快速移动化、界面交互体验优。劣势:缺乏低代码开发功能,模型无法代码化扩展,复杂应用无法支撑;仅支持云端部署,无法私有化部署,费用较高。三、明道云明道云的发展历史很长,从最开始的梅花网到协作SaaS产品明道,最后在2018年转型成为零代码业务应用构建平台。在行业中,还属于比较新的产品,明道云给自己的定位是一个APaaS平台,让企业用户不需要代码开发就能够搭建出用户体验上佳的销售、运营、人事、采购等核心业务应用。从整体功能上看,明道云对标的是国外应用搭建平台Airtable,主要通过搭建数据管理以及流程管理应用,帮助企业高效管理业务。优势:移动化能力强,支持与钉钉和企业微信集成,视图功能较为强大,可扩展性好。劣势:报表功能较弱,审批流偏弱,有一定上手门槛,费用高。四、云程云程平台是一款基于SpringBoot+VUE的低代码开发框架。采用微服务、前后端分离架构,基于可视化流程建模、表单建模、报表建模工具,零代码快速构建云端业务应用 ,平台即可本地化部署,也可基于K8S云原生部署。云程平台也是一款专业的BPM软件,即可独立部署,支撑企业级端到端流程落地,也可嵌入到您的OA、ERP等系统中,作为流程引擎组件使用。云程平台主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量,为企业信息化建设降本增效。云程研发团队核心成员有10年以上的软件研发经验,聚焦于低代码平台、流程引擎等中间件产品研发,我们即可输出云程平台源代码,也可提供专业咨询和定制服务。官方网站: http://www.yunchengxc.com/在线演示:http://www.yunbangong100.com:31110/官方文档:http://www.yunchengxc.com/category/help/doc优势:云原生架构,流程引擎功能强大,对复杂应用支撑较好,云化和私有化均可,可交付源码,商业模式灵活。劣势:缺乏市场宣传,缺乏生态化发展,用户界面美观方面稍有欠缺。五、氚云氚云是深圳奥哲网络科技有限公司(奥哲网络)三大主推产品之一,跟简道云是同时期的选手,已经发展近5年,是一款面向管理者或业务人员的以场景为中心的零(低)代码平台。2018年成为阿里战略投资的低代码应用搭建工具,与钉钉实现了深度融合。 氚云的定位与以流程为中心的H3 BPM和以业务为中心的云枢不同,氚云是以明确的场景为中心,通过可视化表单、流程设计、智能报表和模板化应用,帮助管理者或业务人员快速搭建流程与应用,透明呈现业务进度,实时展现精准数据,完整沉淀管理信息。优势:表单流程配置灵活,应用模板丰富,交互设计体验较好,对钉钉支持较好。劣势:功能较基础,复杂功能需使用低代码,需绑定钉钉提供服务,对于非钉钉类应用,局限性较大。六、JEPaaSJEPaaS是一款优秀的软件平台产品,可视化开发环境,低代码拖拽式配置开发,操作极其简单,可以帮助解决Java项目80%的重复工作,让开发更多关注业务逻辑,大大提高开发效率,能帮助公司大幅节省人力成本和时间成本,同时又不失灵活性。适用于搭建 OA、ERP、CRM、HR、HIS等所有的企业信息管理系统。七、伙伴云伙伴云,是由有着10年以上论坛软件开发及中小企业服务经验的前Discuz!团队打造的,历经“伙伴云表格”、“伙伴办公”,逐步升级为一个为企业经营服务的数据可视化平台,为企业的全流程运营管理与经营核算提供整体解决方案。通过强大的数据库引擎及权限架构,搭配可灵活定制的流程引擎与大数据分析引擎,配合专家级经营顾问与服务,打造全流程、实时可视化的经营核算体系,为企业构建起上下同心的经营伙伴关系,实现员工持续成长、组织持续变革、业绩持续提高。八、道一云道一云|七巧Plus(下面简称“七巧Plus”)是道一云旗下的一款低代码开发平台,去年7月左右上线,算是一款比较新的产品了。七巧Plus给自己的定位是一个高生产力的aPaaS平台,帮助企业快速搭建个性化应用,规范流程管理、数据追踪,提高团队协作效率。 道一云从2004年成立至今,近几年已经陆续发布了基于企业微信的协同应用套件——OA、HR、CRM等应用,在应用平台方面还发布了中台、七巧Plus,从布局上来看,是典型的“PaaS+SaaS”发展模式。九、JeecgBootJeecgBoot 是一款基于代码生成器的低代码开发平台,零代码开发!采用前后端分离架构:SpringBoot2.x,Ant Design&Vue,Mybatis-plus,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码! JeecgBoot引领新的开发模式(Online Coding模式-> 代码生成器模式-> 手工MERGE智能开发), 帮助解决Java项目70%的重复工作,让开发更多关注业务逻辑。既能快速提高开发效率,帮助公司节省成本,同时又不失灵活性!JeecgBoot还独创在线开发模式(No代码概念):在线表单配置(表单设计器)、移动配置能力、工作流配置(在线设计流程)、报表配置能力、在线图表配置、插件能力(可插拔)等等!JeecgBoot开源版本功能有限,只有系统管理功能,低代码开发和流程引擎均需要购买商业版本。十、RuoYiRuoYi是一个后台管理系统,基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf)主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。RuoYi开源版本功能有限,只有系统管理功能,流程引擎等高级组件均需要购买商业版本。
在Docker上安装部署RabbitMQ方便快捷,不需要额外安装Erlang环境,所以写该篇文章先来介绍如何在Docker上部署RabbitMQ。一、安装并运行(1)、在docker hub 中查找rabbitmq镜像docker search rabbitmq:3.9.12-management带有“mangement”的版本,包含web管理页面(2)、从docker hub 中拉取rabbitmq镜像docker pull rabbitmq:3.9.12-management(3)、查看拉取的rabbitmq镜像docker images(4)、运行 rabbitmq服务端docker run -d \ -v /opt/rabbitmq/data:/var/lib/rabbitmq \ -p 5672:5672 -p 15672:15672 --name rabbitmq --restart=always \ --hostname myRabbit rabbitmq:3.9.12-management参数解释: docker run :启动命令 --name :给容器起名字 --restart=always :设置为开机自启 -p : 端口映射,主机端口:容器端口 (5672:应用访问端口;15672:控制台Web端口号) -v : 将主机中指定目录的挂载到容器的目录 -i : 以交互模式运行。 -t : 进入终端。 -d : 以守护模式后台运行。 -e XXX_XXX="xxxxxxxxxxx" : 指定环境变量(5)、查看正在运行的容器docker ps(6)、容器运行成功之后,在浏览器访问:http://192.168.xx.xx:15672账号 guest , 密码 guest二、其他操作(1)、重新启动 rabbitmq 容器docker restart <容器id>(2)、结束正在运行的容器docker stop <容器id> 容器优雅退出 docker kill <容器id> 容器直接退出(3)、删除 docker 容器 (容器在删除前要先结束)docker rm <容器id> [ <容器id> ...](4)、删除 docker 镜像docker rmi <镜像id> [ <镜像id> ...](5)、查看正在运行的 rabbitmq 进程ps -ef | grep rabbitmq(6)、进入容器内部docker exec -it <容器id> /bin/bash(7)、查看容器内网ip地址docker inspect <容器id>(8)、查看docker 镜像的版本docker image inspect <镜像名称>:latest|grep -i version
一、分词简介在Elasticsearch中,假设搜索条件是“华为手机平板电脑”,要求是只要满足了其中任意一个词语组合的数据都要查询出来。借助 Elasticseach 的文本分析功能可以轻松将搜索条件进行分词处理,再结合倒排索引实现快速检索。Elasticseach 提供了三种分词方法:单字分词,二分法分词,词库分词。1、单字分词:如:“华为手机平板电脑”效果:“华”、“为”、“手”、“机”、“平”、“板”、“电”、“脑”2、二分法分词:按两个字进行切分。如:“华为手机平板电脑”效果:“华为”、“为手”、“手机”、“机平”、“平板”、“板电”、“电脑”。3、词库分词:按某种算法构造词,然后去匹配已建好的词库集合,如果匹配到就切分出来成为词语。通常词库分词被认为是最理想的中文分词算法。而词库分词最常用的就是 IK 分词。IK 分词器提供两种分词模式:ik_max_word:会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query。ik_smart:会将文本做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase Query。二、配置IK中文分词器安装好ES和Kibana后,进入Kibana操作页面,使用ES默认的分词器来试一下,中文的分词效果可以看到ES自带的分词器,它是简单的把每个汉字进行了分词,这肯定不是我们想要的效果所以,我们需要自己来配置一下对中文分词支持比较好的IK分词器IK分词器插件下载:(IK分词器要和ES版本一致)7.8.0版:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.0/elasticsearch-analysis-ik-7.8.0.zip其他版本就直接修改url上的版本号//进入plugins目录 cd /mydata/elasticsearch/plugins //新建一个名字叫 ik 的文件夹 mkdir ik //将下载的ik分词器插件压缩包上传到ik文件夹下,然后解压zip到当前目录 unzip elasticsearch-analysis-ik-7.8.0.zip //最后给一下ik文件夹的权限 chmod -R 777 /mydata/elasticsearch/plugins/ik //安装好后,检查下是否安装成功了 使用docker exec -it elasticsearch /bin/bash 命令 进入容器内部 进入容器的 cd /usr/share/elasticsearch/bin 目录 执行 elasticsearch-plugin list 命令(列出es安装的所有插件) //如果列出了 ik 就说明es的ik中文分词器安装成功了 //装好ik分词器后,记得重启ES容器 docker restart elasticsearch配置好IK分词器后,再来用IK分词器看下效果可以看到IK分词器对我们的中文进行了比较理想的分词三、配置自定义分词拓展词库IK分词器对中文有比较好的分词效果,但是它并不能对当前比较热门的网络流行词或者一些新的词进行分词处理上图可以看到,IK分词器并不支持某一些词汇的分词处理所以,我们需要配置一下自定义的分词拓展词库,来支持热门的网络流行词或新词。1、配置自定义词库需要nginx的支持,首先需要安装好nginx2、安装后nginx后,下边开始配置自定义的分词拓展词库[root@192 ~]# cd /mydata/nginx/html/ [root@192 html]# [root@192 html]# mkdir es [root@192 html]# cd es [root@192 html]# vi fenci.txt在/mydata/nginx/html/ 目录下新建一个es文件夹,然后新建一个fenci.txt的文件,在里边写需要支持分词的词汇,然后保存3、配置IK的拓展词库cd /mydata/elasticsearch/plugins/ik/config vim IKAnalyzer.cfg.xml #把里边注释掉的这一句打开并配置成你分词文本所在的nginx访问地址 http://192.168.xx.xx/es/fenci.txt4、重启ES容器docker restart elasticsearch重启后,再来看下效果可以看到已经可以按照自定义的分词拓展词库来进行分词了
一、安装ES1、拉取EalasticSearch镜像docker pull elasticsearch:7.8.02、配置ESmkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data chmod -R 777 /mydata/elasticsearch/编辑配置文件:vim /mydata/elasticsearch/config/elasticsearch.yml 遇到不能创建/修改文件的情况,可以换个文件夹 添加以下内容: cluster.name: "docker-cluster" network.host: 0.0.0.0 http.cors.enabled: true http.cors.allow-origin: "*" 讲解: http.cors.enabled: true //开启跨域 http.cors.allow-origin: "*" //允许所有人访问 3、启动ES注意:下边这个启动es的命令,一定要设置占用的内存,不设置的话,es一启动就把虚拟机的整个内存耗尽了,会死机!!!docker run --name elasticsearch \ -p 9200:9200 -p 9300:9300 \ -e "discovery.type=single-node" \ -e ES_JAVA_OPTS="-Xms64m -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.8.0设置开机启动ESdocker update elasticsearch --restart=always4、测试浏览器输入:http://192.168.xx.xx:9200,看到以下页面说明ES部署成功二、安装Kibana1、拉取Kibana镜像docker pull kibana:7.8.02、启动Kibana注意要修改ES地址docker run --name kibana \ -e ELASTICSEARCH_HOSTS=http://192.168.1.194:9200 \ -p 5601:5601 -d kibana:7.8.0 讲解: -e ELASTICSEARCH_HOSTS 设置elasticsearch地址 设置开机启动Kibana docker update kibana --restart=always3、配置Kibana进入kibana容器接下来使用docker命令进入到kibana容器里面:#进入容器 docker exec -it 容器ID /bin/sh进入容器中找到/usr/share/kibana/config/kibana.yml#使用vi 修改文件内容 vi /usr/share/kibana/config/kibana.yml覆盖配置文件将如下内容写到kibana.yml中,然后保存退出::wqserver.name: kibana server.host: "0" elasticsearch.hosts: [ "http://自己的elasticsearch的IP:9200" ] xpack.monitoring.ui.container.elasticsearch.enabled: true #设置kibana中文显示 i18n.locale: zh-CN4、测试浏览器输入:http://192.168.xx.xx:5601,看到以下页面说明Kibana部署成功三、安装Elasticvue插件1、Elasticvue简介Elasticvue 是用vue开发的Elasticsearch 的免费开源图形化操作插件,可以直接在浏览器中搜索和过滤集群数据。它的功能如下:集群概览索引管理搜索界面查询接口(REST & API 浏览器)快照管理实用程序由于安装 Kibana 可能需要大量的磁盘空间及消耗内存,Elasticvue 适用于一些不太需要 Kibana 那样强大功能的软件。2、安装ElasticvueChrome应用商店:https://chrome.google.com/webstore/detail/elasticvue/hkedbapjpblbodpgbajblpnlpenaebaa?h1=zh3、使用Elasticvue点击插件,进入连接配置界面,输入ES的IP地址进行连接、连接成功后进入首页,展示集群和节点信息点击“节点”,展示节点详细信息点击“索引”,分页展示全部索引信息。可以对索引进行查看、刷新、合并、删除等操作。点击“搜索”,分页展示指定索引的全部文档。点击“REST”,可以发送Restful风格的ES请求。
一、kubectl语法kubectl [command] [TYPE] [NAME] [flags]官网地址:https://kubernetes.io/docs/reference/kubectl/overview/kubectl flags:https://kubernetes.io/docs/reference/kubectl/kubectl/说明:1、command:指定在一个或多个资源上要执行的操作。例如:create、get、describe、delete、apply等2、TYPE:指定资源类型(如:pod、node、services、deployments等)。资源类型大小写敏感,可以指定单数、复数或缩写形式。例如,以下命令生成相同的输出:kubectl get pod -n kubernetes-dashboard kubectl get pods -n kubernetes-dashboard kubectl get po -n kubernetes-dashboard3、NAME:指定资源的名称。名称大小写敏感。如果省略名称空间,则显示默认名称空间资源的详细信息或者提示:No resources found in default namespace(在默认命名空间中没有找到该资源)。# 示例: [root@k8s-master ~]# kubectl get pods No resources found in default namespace. [root@k8s-master ~]# kubectl get pods --all-namespaces # 或者 kubectl get pods --A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-6955765f44-c9zfh 1/1 Running 8 6d7h kube-system coredns-6955765f44-lrz5q 1/1 Running 8 6d7h kube-system etcd-k8s-master 1/1 Running 9 6d7h kube-system kube-apiserver-k8s-master 1/1 Running 9 6d7h kube-system kube-controller-manager-k8s-master 1/1 Running 8 6d7h kube-system kube-flannel-ds-amd64-dngrk 1/1 Running 13 6d7h kube-system kube-flannel-ds-amd64-h4sn6 1/1 Running 13 6d6h kube-system kube-flannel-ds-amd64-m92wp 1/1 Running 11 6d6h kube-system kube-proxy-28dwj 1/1 Running 9 6d6h kube-system kube-proxy-c875m 1/1 Running 8 6d7h kube-system kube-proxy-stg6w 1/1 Running 10 6d6h kube-system kube-scheduler-k8s-master 1/1 Running 9 6d7h kubernetes-dashboard dashboard-metrics-scraper-7b8b58dc8b-nr5fz 1/1 Running 7 6d1h kubernetes-dashboard kubernetes-dashboard-755dcb9575-9kg7p 1/1 Running 9 6d1h [root@k8s-master ~]# kubectl get service --all-namespaces # 或者 kubectl get service -A NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d7h kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 6d7h kubernetes-dashboard dashboard-metrics-scraper ClusterIP 10.104.12.221 <none> 8000/TCP 6d1h kubernetes-dashboard kubernetes-dashboard NodePort 10.110.157.29 <none> 443:30001/TCP 6d1h3、flags:指定可选的标记。例如,可以使用 -s 或 --server标识来指定Kubernetes API服务器的地址和端口;-n指定名称空间;等等。注意:你从命令行指定的flags将覆盖默认值和任何相应的环境变量。优先级最高。4、在多个资源上执行操作时,可以通过类型 [TYPE] 和名称 [NAME] 指定每个资源,也可以指定一个或多个yml文件。按类型和名称指定资源:# 查看一个资源类型中的多个资源 [root@k8s-master ~]# kubectl get pod -n kube-system coredns-6955765f44-c9zfh kube-proxy-28dwj NAME READY STATUS RESTARTS AGE coredns-6955765f44-c9zfh 1/1 Running 8 6d7h kube-proxy-28dwj 1/1 Running 9 6d6h [root@k8s-master ~]# # 查看多个资源类型 [root@k8s-master ~]# kubectl get svc,node NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 45h NAME STATUS ROLES AGE VERSION node/k8s-master Ready master 45h v1.17.4 node/k8s-node01 Ready <none> 45h v1.17.4 node/k8s-node02 Ready <none> 45h v1.17.4使用一个或多个文件指定资源:-f file1 -f file2 -f fileN# 使用YAML而不是JSON,因为YAML更容易使用,特别是对于配置文件。 kubectl get pod -f pod.yaml二、子命令详解1、command2、type3、flags4、kubectl的输出格式kubect支持结果以多种格式输出显示,使用-o 参数指定自定义字段输出kubectl [command] [type] -o=<output_format>三、kubectl常用命令1、查看类命令# 获取节点和服务版本信息 kubectl get nodes # 获取节点和服务版本信息,并查看附加信息 kubectl get nodes -o wide # 获取pod信息,默认是default名称空间 kubectl get pod # 获取pod信息,默认是default名称空间,并查看附加信息【如:pod的IP及在哪个节点运行】 kubectl get pod -o wide # 获取指定名称空间的pod kubectl get pod -n kube-system # 获取指定名称空间中的指定pod kubectl get pod -n kube-system podName # 获取所有名称空间的pod kubectl get pod -A # 查看pod的详细信息,以yaml格式或json格式显示 kubectl get pods -o yaml kubectl get pods -o json # 查看pod的标签信息 kubectl get pod -A --show-labels # 根据Selector(label query)来查询pod kubectl get pod -A --selector="k8s-app=kube-dns" # 查看运行pod的环境变量 kubectl exec podName env # 查看指定pod的日志 kubectl logs -f --tail 500 -n kube-system kube-apiserver-k8s-master # 查看所有名称空间的service信息 kubectl get svc -A # 查看指定名称空间的service信息 kubectl get svc -n kube-system # 查看componentstatuses信息 kubectl get cs # 查看所有configmaps信息 kubectl get cm -A # 查看所有serviceaccounts信息 kubectl get sa -A # 查看所有daemonsets信息 kubectl get ds -A # 查看所有deployments信息 kubectl get deploy -A # 查看所有replicasets信息 kubectl get rs -A # 查看所有statefulsets信息 kubectl get sts -A # 查看所有jobs信息 kubectl get jobs -A # 查看所有ingresses信息 kubectl get ing -A # 查看有哪些名称空间 kubectl get ns # 查看pod的描述信息 kubectl describe pod podName kubectl describe pod -n kube-system kube-apiserver-k8s-master # 查看指定名称空间中指定deploy的描述信息 kubectl describe deploy -n kube-system coredns # 查看node或pod的资源使用情况 # 需要heapster 或metrics-server支持 kubectl top node kubectl top pod # 查看集群信息 kubectl cluster-info 或 kubectl cluster-info dump # 查看各组件信息【172.16.1.110为master机器】 kubectl -s https://172.16.1.110:6443 get componentstatuses2、操作类命令# 创建资源 kubectl create -f xxx.yaml # 应用资源 kubectl apply -f xxx.yaml # 应用资源,该目录下的所有 .yaml, .yml, 或 .json 文件都会被使用 kubectl apply -f <directory> # 创建test名称空间 kubectl create namespace test # 删除资源 kubectl delete -f xxx.yaml kubectl delete -f <directory> # 删除指定的pod kubectl delete pod podName # 删除指定名称空间的指定pod kubectl delete pod -n test podName # 删除其他资源 kubectl delete svc svcName kubectl delete deploy deployName kubectl delete ns nsName # 强制删除 kubectl delete pod podName -n nsName --grace-period=0 --force kubectl delete pod podName -n nsName --grace-period=1 kubectl delete pod podName -n nsName --now # 编辑资源 kubectl edit pod podName3、其他操作# kubectl exec:进入pod启动的容器 kubectl exec -it podName -n nsName /bin/sh #进入容器 kubectl exec -it podName -n nsName /bin/bash #进入容器 # kubectl label:添加label值 kubectl label nodes k8s-node01 zone=north #为指定节点添加标签 kubectl label nodes k8s-node01 zone- #为指定节点删除标签 kubectl label pod podName -n nsName role-name=test #为指定pod添加标签 kubectl label pod podName -n nsName role-name=dev --overwrite #修改lable标签值 kubectl label pod podName -n nsName role-name- #删除lable标签 # kubectl滚动升级; 通过 kubectl apply -f myapp-deployment-v1.yaml 启动deploy kubectl apply -f myapp-deployment-v2.yaml #通过配置文件滚动升级 kubectl set image deploy/myapp-deployment myapp="registry.cn-beijing.aliyuncs.com/google_registry/myapp:v3" #通过命令滚动升级 kubectl rollout undo deploy/myapp-deployment 或者 kubectl rollout undo deploy myapp-deployment #pod回滚到前一个版本 kubectl rollout undo deploy/myapp-deployment --to-revision=2 #回滚到指定历史版本 # kubectl scale:动态伸缩 kubectl scale deploy myapp-deployment --replicas=5 # 动态伸缩 kubectl scale --replicas=8 -f myapp-deployment-v2.yaml #动态伸缩【根据资源类型和名称伸缩,其他配置「如:镜像版本不同」不生效】
一、Rancher概述1.1 什么是RancherRancher 是为使用容器的公司打造的容器管理平台。Rancher 简化了使用 Kubernetes 的流程,方便开发者可以随处运行 Kubernetes(Run Kubernetes Everywhere),以便于满足 IT 需求规范,赋能 DevOps 团队。当然 Rancher 2.x 已经完全转向了 Kubernetes。Rancher 2.x 可以部署和管理在任何地方运行的 Kubernetes 集群。Rancher 可以创建来自 Kubernetes 托管服务提供商的集群,自动创建节点并安装 Kubernetes 集群,或者导入任何已经存在的 Kubernetes 集群。Rancher 通过支持集群的身份验证和基于角色的访问控制(RBAC),使系统管理员能够从一个位置控制全部集群的访问。Rancher 可以对集群及其资源进行详细的监控和并在需要时发送告警,也可以将容器日志发送给外部日志系统,并通过应用商店与 Helm 集成。若具有外部 CI/CD 流水线系统,则可以将其与 Rancher 对接,如果没有,Rancher 也提供了简单易用的流水线实现自动部署和升级工作负载。除此之外,Rancher 还有很多开箱即用的功能用于更好的管理集群和业务应用,例如多集群应用,全局 DNS,服务网格,安全扫描,集群模版和基于 OPA 的策略管理等功能。综上所述,Rancher 是一个全栈式的 Kubernetes 容器管理平台,也是一个可以在任何地方都能成功运行 Kubernetes 的工具。1.2 Rancher Server功能Rancher API Server 是基于嵌入式 Kubernetes API Server 和 ETCD 数据库建立的,它提供了以下功能:授权和角色权限控制用户管理: Rancher API server 除了管理用户在公司内部的使用的认证信息之外,还管理用户访问外部服务所需的认证信息,如登录活动目录或 GitHub 所需的账号密码。授权: Rancher API server 负责管理权限控制策略 和 安全策略。基于Rancher使用 Kubernetes 的功能运行 Kubernetes 集群: Rancher API server 可以在已有节点上运行 Kubernetes 集群 ,或对 Kubernetes 进行版本升级。应用商店管理: Rancher 可以使用Helm Charts 应用商店重复部署应用。项目管理: 项目,是 Rancher 中的一个概念,Kubernetes 中并没有这个概念。项目由一个集群内的多个命名空间和多个访问控制策略组成,允许用户以组为单位,一次管理多个命名空间,对其进行 Kubernetes 相关操作。Rancher 用户界面提供了 项目管理 和 项目内应用管理 两个功能。流水线: 流水线 可以帮助开发者快速高效地上线新软件。Rancher 支持给每一个项目单独配置流水线。Istio: Rancher 与 Istio 集成,管理员或集群所有者可以将 Istio 交给开发者,然后开发者使用 Istio 执行安全策略,排查问题,或为快速发布、灰度发布和 A/B 测试进行流量控制。配置云端基础信息同步节点信息: Rancher API server 可以同步集群内所有节点的信息。配置云端基础信息: 当 Rancher 与云服务提供商配置完了之后,可以在云端动态配置新节点和持久化存储。查看集群信息日志: Rancher 可以跟多种主流日志工具集成,可以设置 集群日志 或 项目日志。监控: 使用 Rancher,可以通过 Prometheus 监控集群节点、Kubernetes 组件、软件部署的状态和进度。可以设置 集群监控 或 项目监控。告警信息: 需要随时知道集群和项目的计划和非计划事件,才可以提高公司的运行效率。可以设置集群告警 或 项目告警。1.3 Rancher架构设计Rancher Server 由认证代理(Authentication Proxy)、Rancher API Server、集群控制器(Cluster Controller)、etcd 节点和集群 Agent(Cluster Agent) 组成。除了集群 Agent 以外,其他组件都部署在 Rancher Server 中。如上图所示用户通过 Rancher Server 管控 Rancher 部署的 Kubernetes 集群(RKE 集群)和托管的 Kubernetes 集群的(EKS)集群的流程。以用户下发指令为例,指令的流动路径如下:首先,用户通过 Rancher UI(即 Rancher 控制台)、Rancher API 接口、Rancher 命令行工具(Rancher CLI)输入指令;用户通过 Rancher 的代理认证后,指令会进一步下发到 Rancher Server;与此同时,Rancher Server 也会执行容灾备份,将数据备份到 etcd 节点;然后 Rancher Server 把指令传递给集群控制器。集群控制器把指令传递到下游集群的 Agent,最终通过 Agent 把指令下发到指定的集群中。如果 Rancher Server 出现问题,我们也提供了备用方案,可以通过授权集群端点管理集群。提示:考虑到性能和安全因素,通常建议使用两个 Kubernetes 集群,分开用于部署 Rancher Server 和生产环境。部署 Rancher Server 后,可以创建或导入集群,然后使用Rancher Server创建用于生产的 Kubernetes 集群。提示:可以在单个节点或高可用的 Kubernetes 集群上安装 Rancher。由于单节点安装只适用于开发和测试环境,而且单节点和高可用集群之间无法进行数据迁移,所以建议从一开始就使用高可用的 Kubernetes 集群来部署 Rancher Server,而且需要分开部署运行 Rancher Server 的集群和运行业务的生产 Kubernetes 集群。1.4 Rancher和下游集群交互如下图示例,通过两个用户 Bob 和 Alice,理解 Rancher 启动和管理下游集群的具体过程,以及每个 Rancher 组件的作用。该示例涉及集群控制器、集群 Agent 和 Node Agent等组件。认证代理如上视图中 Bob 的用户希望查看下游集群“User Cluster 1”里面正在运行的 pod。Bob 发起的请求会首先经过认证代理,通过认证之后,Rancher 的 认证代理才会把 API 调用命令转发到下游集群。认证代理集成了多种认证方式,如本地认证、活动目录认证、GitHub 认证等。在发起每一个 Kubernetes API 调用请求的时候,认证代理会去确认请求方的身份,在转发调用命令前,请设置正确的 Kubernetes impersonation 的消息头。Rancher 使用 Service Account (Service Accout 提供了一种方便的认证机制)和 Kubernetes 进行交互。默认状态下,Rancher 生成一个包含认证信息的kubeconfig文件,为 Rancher Server 和下游集群的 Kubernetes API Server 之间的通信提供认证。该文件包含了访问集群的所有权限。集群控制器和集群 Agent每一个下游集群都有一个集群 Agent 保持下游集群的集群控制器与 Rancher Server 之间的信息畅通。集群控制器具有以下功能:检测下游集群的资源变化,如内存使用率、CPU 使用率等;把下游集群从“当前”状态变更到“目标”状态;配置集群和项目的访问控制策略;通过调用 Docker Machine 和 Kubernetes Engine,如 RKE 和 GKE,创建集群。提示:默认状态下,集群控制器连接 Agent,Rancher 才可以与下游集群通信。如果集群 Agent 不可用,集群控制器可以连接到Node Agent,通过Node Agent 实现用户和集群之间的通信。集群 Agent,也叫做“cattle-cluster-agent”,是在下游集群中运行的组件,它具有以下功能:连接使用 Rancher 部署的 Kubernetes 集群(RKE 集群)中的 Kubernetes API;管理集群内的工作负载,pod 创建和部署;根据每个集群的设置,配置 Role 和 RoleBindings;实现集群和 Rancher Server 之间的消息传输,包括事件,指标,健康状况和节点信息等。节点 Agents如果集群 Agent 不可用,下游集群中的其中一个节点 Agent 会创建一个通信管道,由节点 Agent 连接到集群控制器,实现下游集群和 Rancher 之间的通信。部署节点 Agent 的方式有很多,通常建议使用DaemonSet部署节点 Agent ,这种方式可以确保下游集群内每个节点都成功运行节点 Agent。执行集群操作时,可以使用这种方式将指令下发到下游集群。集群操作包括:升级 Kubernetes 版本、创建 etcd 节点备份和恢复 etcd 节点。授权集群端点Rancher Server 和下游集群之间有明显的延迟,或 Rancher Server 不可用时,用户可以通过授权集群端点连接下游集群,实现 Rancher Server 和集群之间的通信,降低网络延迟。需要注意的是,只有 Rancher 部署的 Kubernetes 集群(RKE 集群)可以使用授权集群端点这个功能。其他类型的集群,如导入的集群、托管的集群等,并不能够使用此功能。kube-api-auth 微服务向授权集群端点提供了用户认证功能。使用 kubectl 访问下游集群时,集群的 Kubernetes API Server 通过 kube-api-auth 对用户进行认证。与授权集群端点类似, kube-api-auth 认证功能只在 Rancher 部署的 Kubernetes 集群(RKE 集群)中有效。使用场景举例:假设 Rancher Server 位于美国,用户“Alice”和她管理的下游集群“User Cluster 1”位于澳大利亚。虽然 Alice 可以使用 Rancher 控制台管理 User Cluster 1 中的资源,但是她发出的请求要从澳大利亚发送到美国的 Server 端,然后再由 Server 代理回澳大利亚的集群端,澳大利亚集群端处理完请求后,再返回给美国的 Server 端,最后才能返回给澳大利亚的“Alice”。因为美澳之间的距离非常遥远,所以发送的请求和返回的请求结果都会存在显著的延迟。Alice 可以使用授权集群端点,降低延迟,更好地掌控她的下游集群。为下游集群开启授权集群端点后,Rancher 会在“kubeconfig”文件中额外生成一段 Kubernetes context,来允许用户直接连接到集群。kubeconfig 这个文件中含有 kubectl 和 helm 的认证信息。如果 Rancher 出现问题,无法连接,需要使用 kubeconfig 中的 context 帮助访问集群。因此,建议导出一份 kubeconfig 文件副本,保存到本地,以备不时之需。1.5 Rancher相关文件rancher-cluster.yml :RKE 集群配置文件。kube_config_rancher-cluster.yml :集群的 kubeconfig 文件,它包含了访问集群的全部权限。如果 Rancher 出现故障,无法运行,可以使用这个文件连接通过 Rancher 部署的 Kubernetes 集群(RKE 集群)。rancher-cluster.rkestate :Kubernetes 集群状态文件,该文件含有访问集群的所有权限。只有使用 RKE 0.2.0 或以上版本时,才会创建该文件。注意:后两个文件名的“rancher-cluster”部分取决于如何命名 RKE 集群的配置文件。1.6 Rancher创建的Kubernetes类型Rancher通常可以创建如下几种类型的下游 Kubernetes 集群:Rancher自动创建节点并部署Kubernetes集群:Rancher 可以动态创建位于云上的节点(Node),如 Amazon EC2、Azure 和 vSphere等,然后在节点上安装 Kubernetes。Rancher 使用 RKE 和 docker-machine创建这种集群。Rancher通过已有的节点并部署Kubernetes集群:配置这种集群时,Rancher 可以在已有的虚拟机、物理机或云主机上安装 Kubernetes。这种集群叫自定义集群。Rancher 使用RKE创建这种集群。Rancher管理云服务商托管的Kubernetes集群:配置这种集群时,Kubernetes 由云服务供应商安装,如 GKE、ECS 和 AKS。Rancher 使用kontainer-engine来调用云厂商的 API 来创建集群。Rancher导入已有的Kubernetes集群:这种情况下,Rancher 只需要连接到已经配置好 Kubernetes 的集群。因此,Rancher 只设置 Rancher Agent 与集群通信,不直接创建集群。二、优点和不足2.1 优点1、采用图形化方式:易用的Web管理界面,在Docker易用性的基础上,再一次降低了使用容器技术部署容器应用的难度。2、支持多种调度器:通过环境模板,很容易地创建和部署Cattle、Swarm、K8S、Mesos容器集群管理调度平台。3、管理主机集群:管理对象是多台主机的集群,而不仅仅是单台容器主机,创建和管理几台、几十台应用服务器集群,只需要Ctrl+C、Ctrl+V,复制粘贴就解决了。4、内置应用商店:使用其中的WordPress模板,创建一个WordPress博客系统,只需点点鼠标,部署难度不高于申请一个免费的电子邮箱!5、资源弹性分配:内置应用负载均衡器,“服务”最小只需1个容器实例,当负载不够/或过剩时,只需点点鼠标,增加/减少“服务”中容器的实例数,即可解决问题,应用系统具有天生的弹性扩容能力。…2.2 不足1、K8S部署的问题:由于国内网络和国外网络访问的问题,在国内部署K8S集群,有点不方便;2、应用商店的问题:默认的Rancher官方认证和社区贡献的应用商店内容有限,应用还不够丰富,基本上都是网络和存储等基础的应用;个别应用部署后,无法通过相同的操作,再部署同样的另一套应用,比如Rancher NFS;
一、WebSocket协议是什么WebSocket是基于TCP的应用层协议,用于在C/S架构的应用中实现双向通信,它实现了浏览器与服务器全双工(full-duplex)通信,也就是允许服务器主动发送信息给客户端。WebSocket 协议主要为了解决基于 HTTP/1.x 的 Web 应用无法实现服务端向客户端主动推送的问题, 为了兼容现有的设施, WebSocket 协议使用与 HTTP 协议相同的端口, 并使用 HTTP Upgrade 机制来进行 WebSocket 握手, 当握手完成之后, 通信双方便可以按照 WebSocket 协议的方式进行交互。需要特别注意的是:虽然WebSocket协议在建立连接时会使用HTTP协议,但这并意味着WebSocket协议是基于HTTP协议实现的。二、WebSocket与Http的区别实际上,WebSocket协议与Http协议有着本质的区别:1.通信方式不同WebSocket是双向通信模式,客户端与服务器之间只有在握手阶段是使用HTTP协议的“请求-响应”模式交互,而一旦连接建立之后的通信则使用双向模式交互,不论是客户端还是服务端都可以随时将数据发送给对方;而HTTP协议则至始至终都采用“请求-响应”模式进行通信。也正因为如此,HTTP协议的通信效率没有WebSocket高。2.协议格式不同WebSocket与HTTP的协议格式是完全不同的,具体来讲:(1)HTTP协议(参见:rfc2616)比较臃肿,而WebSocket协议比较轻量。(2)对于HTTP协议来讲,一个数据包就是一条完整的消息;而WebSocket客户端与服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。即:发送端将消息切割成多个帧,并发送给服务端;服务端接收消息帧,并将关联的帧重新组装成完整的消息。WebSocket协议格式: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+ HTTP请求消息格式:Request-LineCRLF general-headerCRLF request-headerCRLF entity-headerCRLF CRLF [ message-body ]HTTP响应消息格式:Status-LineCRLF general-headerCRLF response-headerCRLF entity-headerCRLF CRLF [ message-body ] 虽然WebSocket和HTTP是不同应用协议,但rfc6455规定:“WebSocket设计为通过80和443端口工作,以及支持HTTP代理和中介”,从而使其与HTTP协议兼容。为了实现兼容性,WebSocket握手时使用HTTP Upgrade头从HTTP协议更改为WebSocket协议。三、为什么要使用WebSocket随着Web应用的发展,特别是动态网页的普及,越来越多的场景需要实现数据动态刷新。在早期的时候,实现数据刷新的方式通常有如下3种:1.客户端定时查询客户端定时查询(如:每隔10秒钟查询一次)是最原始也是最简单的实现数据刷新的方法,服务端不用做任何改动,只需要在客户端添加一个定时器即可。但是这种方式的缺点也很明显:大量的定时请求都是无效的,因为服务端的数据并没有更新,相应地也导致了大量的带宽浪费。2.长轮训机制长轮训机制是对客户端定时查询的一种改进,即:客户端依旧保持定时发送请求给服务端,但是服务端并不立即响应,而是等到真正有数据更新的时候才发送给客户端。实际上,并不是当没有数据更新时服务端就永远都不响应客户端,而是需要在等待一个超时时间之后结束该次长轮训请求。相对于客户端定时查询方式而言,当数据更新频率不确定时长轮训机制能够很明显地减少请求数。但是,在数据更新比较频繁的场景下,长轮训方式的优势就没那么明显了。在Web开发中使用得最为普遍的长轮训实现方案为Comet(Comet (web技术)),Tomcat和Jetty都有对应的实现支持,详见:WhatIsComet,Why Asynchronous Servlets。3.HTTP Streaming不论是长轮训机制还是传统的客户端定时查询方式,都需要客户端不断地发送请求以获取数据更新,而HTTP Streaming则试图改变这种方式,其实现机制为:客户端发送获取数据更新请求到服务端时,服务端将保持该请求的响应数据流一直打开,只要有数据更新就实时地发送给客户端。虽然这个设想是非常美好的,但这带来了新的问题:(1)HTTP Streaming的实现机制违背了HTTP协议本身的语义,使得客户端与服务端不再是“请求-响应”的交互方式,而是直接在二者建立起了一个单向的“通信管道”。(2)在HTTP Streaming模式下,服务端只要得到数据更新就发送给客户端,那么就需要客户端与服务端协商如何区分每一个更新数据包的开始和结尾,否则就可能出现解析数据错误的情况。(3)另外,处于客户端与服务端的网络中介(如:代理)可能会缓存响应数据流,这可能会导致客户端无法真正获取到服务端的更新数据,这实际上与HTTP Streaming的本意是相违背的。鉴于上述原因,在实际应用中HTTP Streaming并没有真正流行起来,反之使用得最多的是长轮训机制。显然,上述几种实现数据动态刷新的方式都是基于HTTP协议实现的,或多或少地存在这样那样的问题和缺陷;而WebSocket是一个全新的应用层协议,专门用于Web应用中需要实现动态刷新的场景。相比起HTTP协议,WebSocket具备如下特点:支持双向通信,实时性更强。更好的二进制支持。较少的控制开销:连接创建后,WebSockete客户端、服务端进行数据交换时,协议控制的数据包头部较小。支持扩展。四、websocket的用途场景高即时性服务,比如聊天室的群聊,server顺序收到了张三,李四的消息,立即就推送给王五,不能让王五等半天。Ajax也可以一秒一刷,让王五去问张三说话没,如果张三10分钟没说话,王五要去问600次。 用websocket 王五不用刷,等着就好了,服务器有了消息会自动给王五的。1.websocket社交订阅对社交类的应用的一个裨益之处就是能够即时的知道你的朋友正在做什么。虽然听起来有点可怕,但是我们都喜欢这样做。你不会想要在数分钟之后才能知道一个家庭成员在馅饼制作大赛获胜或者一个朋友订婚的消息。你是在线的,所以你的订阅的更新应该是实时的。2.websocket多玩家游戏网络正在迅速转变为游戏平台。在不使用插件(我指的是Flash)的情况下,网络开发者现在可以在浏览器中实现和体验高性能的游戏。无论你是在处理DOM元素、css动画,html5的canvas或者尝试使用WebGL,玩家之间的互动效率是至关重要的。我不想在我扣动扳机之后,我的对手却已经移动位置。3.websocket协同编辑/编程我们生活在分布式开发团队的时代。平时使用一个文档的副本就满足工作需求了,但是你最终需要有一个方式来合并所有的编辑副本。版本控制系统,比如Git能够帮助处理某些文件,但是当Git发现一个它不能解决的冲突时,你仍然需要去跟踪人们的修改历史。通过一个协同解决方案,比如WebSocket,我们能够工作在同一个文档,从而省去所有的合并版本。这样会很容易看出谁在编辑什么或者你在和谁同时在修改文档的同一部分。用户打开文档编辑页面,与后端建立长连接。 后端将当前用户加入当前文档编辑用户列表。 前端监听用户对于文档内容的修改,每一次修改将整个修改内容发送给后端。 后端接收到信息,不做任何处理,直接将文本信息发送给文档编辑用户列表中其他的所有用户。 前端收到后端的文本信息直接覆盖掉当前文档内容。4.websocket收集点击流数据分析用户与你网站的互动是提升你的网站的关键。HTTP的开销让我们只能优先考虑和收集最重要的数据部分。然后,经过六个月的线下分析,我们意识到我们应该收集一个不同的判断标准——一个看起来不是那么重要但是现在却影响了一个关键的决定。与HTTP请求的开销方式相比,使用Websocket,你可以由客户端发送不受限制的数据。想要在除页面加载之外跟踪鼠标的移动?只需要通过WebSocket连接发送这些数据到服务器,并存储在你喜欢的NoSQL数据库中就可以了(MongoDB是适合记录这样的事件的)。现在你可以通过回放用户在页面的动作来清楚的知道发生了什么。5.股票基金报价金融界瞬息万变——几乎是每毫秒都在变化。我们人类的大脑不能持续以那样的速度处理那么多的数据,所以我们写了一些算法来帮我们处理这些事情。虽然你不一定是在处理高频的交易,但是,过时的信息也只能导致损失。当你有一个显示盘来跟踪你感兴趣的公司时,你肯定想要随时知道他们的价值,而不是10秒前的数据。使用WebSocket可以流式更新这些数据变化而不需要等待。6.体育实况更新现在我们开始讨论一个让人们激情澎湃的愚蠢的东西——体育。我不是运动爱好者,但是我知道运动迷们想要什么。当爱国者在打比赛的时候,我的妹夫将会沉浸于这场比赛中而不能自拔。那是一种疯狂痴迷的状态,完全发自内心的。我虽然不理解这个,但是我敬佩他们与运动之间的这种强烈的联系,所以,最后我能做的就是给他的体验中降低延迟。如果你在你的网站应用中包含了体育新闻,WebSocket能够助力你的用户获得实时的更新。7.多媒体聊天视频会议并不能代替和真人相见,但当你不能在同一个屋子里见到你谈话的对象时,视频会议是个不错的选择。尽管视频会议私有化做的“不错”,但其使用还是很繁琐。我可是开放式网络的粉丝,所以用WebSockets getUserMedia API和html5音视频元素明显是个不错的选择。WebRTC的出现顺理成章的成为我刚才概括的组合体,它看起来很有希望,但其缺乏目前浏览器的支持,所以就取消了它成为候选人的资格。8.基于位置的应用越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹),你可以收集到更加细致化的数据。如果你想实时的更新网络数据仪表盘(可以说是一个监视运动员的教练),HTTP协议显得有些笨拙。借用WebSocket TCP链接可以让数据飞起来。
垃圾回收算法是内存回收的方法论,垃圾回收器是内存回收的实践者。不同的垃圾回收器有不同的特性,并没有一个万能或最好的垃圾回收器,只能根据不同的业务场景选择最合适的垃圾回收器,所以这节就来了解下各个垃圾回收器的特性。1、Stop The World(STW)先看看jvm的 “Stop The World” 问题。1)STW:可达性分析算法从 GC Roots 集合找引用链时,需要枚举根节点,然后从根节点标记存活的对象,根节点枚举以及整理内存碎片时,都会发生 Stop The World,此时 jvm 会直接暂停应用程序的所有用户线程,然后进行垃圾回收。因为垃圾回收时如果还在继续创建对象或更新对象引用,就会导致这些对象可能无法跟踪和回收、跟节点不断变化等比较复杂的问题,因此垃圾回收过程必须暂停所有用户线程,进入 STW 状态。垃圾回收完成后,jvm 会恢复应用程序的所有用户线程。所有垃圾回收器都无法避免 STW,只能尽量缩短用户线程的停顿时间。系统停顿期间,无法处理任何请求,所有用户请求都会出现短暂的卡顿。如果因为内存分配不合理或垃圾回收器使用不合理,导致频繁的垃圾回收,而且每次回收系统停顿时间过长,这会让用户体验极差。jvm 最重要的一个优化就是通过合理的内存分配,使用合适的垃圾回收器,使得垃圾回收频率最小、停顿时间最短,避免影响系统正常运行。2)安全点(Safe Point):用户程序执行时并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到达安全点后才能够暂停。安全点可以理解成是在代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,说明虚拟机当前的状态是安全的,如果有需要,可以在这个位置暂停。安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”为标准进行选定的,“长时间执行”的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。jvm 采用主动式中断的方式,在垃圾回收发生时让所有线程都跑到最近的安全点。主动式中断的思想是当垃圾回收需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志位,各个线程执行过程时会不停地主动去轮询这个标志,一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起。3)安全区域(Safe Region):安全点机制保证了程序执行时,在不太长的时间内就会遇到可进入垃圾回收过程的安全点。但是,程序“不执行”的时候,线程就无法响应虚拟机的中断请求,如用户线程处于Sleep状态或者Blocked状态,这个时候就没法再走到安全的地方去中断挂起自己。这就需要安全区域来解决了。安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾回收都是安全的。当用户线程执行到安全区域里面的代码时,首先会标识自己已经进入了安全区域,那样当这段时间里虚拟机要发起垃圾回收时就不必去管这些已声明自己在安全区域内的线程了。当线程要离开安全区域时,它要检查虚拟机是否已经完成了需要暂停用户线程的阶段,如果完成了,那线程就继续执行;否则它就必须一直等待,直到收到可以离开安全区域的信号为止。2、Serial 垃圾回收器Serial 垃圾回收器是一个单线程回收器,它进行垃圾回收时,必须暂停其他所有用户线程,直到它回收结束。Serial 主要用于新生代垃圾回收,采用复制算法实现。服务端程序几乎不会使用 Serial 回收器,服务端程序一般会分配较大的内存,可能几个G,如果使用 Serial 回收器,由于是单线程,标记、清理阶段就会花费很长的时间,就会导致系统较长时间的停顿。Serial 一般用在客户端程序或占用内存较小的微服务,因为客户端程序一般分配的内存都比较小,可能几十兆或一两百兆,回收时的停顿时间是完全可以接受的。而且 Serial 是所有回收器里额外消耗内存最小的,也没有线程切换的开销,非常简单高效。3、Serial Old 垃圾回收器Serial Old 是 Serial 的老年代版本,它同样是一个单线程回收器,主要用于客户端程序。Serial Old 用于老年代垃圾回收,采用标记-整理算法实现。Serial Old 也可以用在服务端程序,主要有两种用途:一种是与 Parallel Scavenge 回收器搭配使用,另外一种就是作为 CMS 回收器发生失败时的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。4、ParNew 垃圾回收器ParNew 回收器实质上是 Serial 回收器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为都与 Serial 回收完全一致,控制参数、回收算法、对象分配规则等都是一致的。除了 Serial 回收器外,目前只有 ParNew 回收器能与 CMS 回收器配合工作,ParNew 是激活CMS后的默认新生代回收器。ParNew 默认开启的回收线程数与处理器核心数量相同,在处理器核心非常多的环境中,可以使用 -XX: ParallelGCThreads 参数来限制垃圾回收的线程数。5、Parallel Scavenge 垃圾回收器Parallel Scavenge是新生代回收器,采用复制算法实现,也是能够并行回收的多线程回收器。Parallel Scavenge 主要关注可控制的吞吐量,其它回收器的关注点是尽可能地缩短垃圾回收时的停顿时间。吞吐量就是处理器用于运行程序代码的时间与处理器总消耗时间的比值,总消耗时间等于运行程序代码的时间加上垃圾回收的时间。Parallel Scavenge 提供了两个参数用于精确控制吞吐量:-XX: MaxGCPauseMillis:控制最大垃圾回收停顿时间,参数值是一个大于0的毫秒数,回收器将尽力保证垃圾回收花费的时间不超过这个值。-XX: GCTimeRatio:直接设置吞吐量大小,参数值是一个大于0小于100的整数,就是垃圾回收时间占总时间的比率。默认值为 99,即允许最大1%(即1/(1+99))的垃圾收集时间。Parallel Scavenge 还有一个参数 -XX: +UseAdaptiveSizePolicy,当设置这个参数之后,就不需要人工指定新生代的大小、Eden与Survivor区的比例等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。6、Parallel Old 垃圾回收器Parallel Old 是 Parallel Scavenge 的老年代版本,支持多线程并发回收,采用标记-整理算法实现。在注重吞吐量或者处理器资源较为稀缺的场合,可以优先考虑 Parallel Scavenge 加 Parallel Old 这个组合。7、CMS 垃圾回收器CMS(Concurrent Mark Sweep)是一种以获取最短回收停顿时间为目标的回收器。CMS 用于老年代垃圾回收,采用标记-清除算法实现。1)CMS 回收过程:CMS 垃圾回收总体分为四个步骤:1)初始标记(会STW):初始标记需要 Stop The World,初始标记仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快。2)并发标记:并发标记阶段就是从 GC Roots 的直接关联对象开始遍历整个对象引用链的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾回收线程一起并发运行。3)重新标记(会STW):重新标记需要 Stop The World,重新标记阶段是为了修正并发标记期间,因程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。4)并发清除:清除阶段是清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发进行的。最耗时的并发标记和并发清除阶段是和用户线程并发进行的,总体上来说,CMS 回收过程是与用户线程一起并发执行的,是一款并发低停顿的回收器。2)触发CMS的条件CMS GC 在实现上分成 foreground collector 和 background collector。① foreground collectorforeground collector 触发条件比较简单,一般是遇到对象分配但空间不够,就会直接触发 GC,来立即进行空间回收。采用的算法是 mark sweep,不压缩。② background collectorbackground collector 是通过 CMS 后台线程不断的去扫描,过程中主要是判断是否符合 background collector 的触发条件,一旦有符合的情况,就会进行一次 background 的 collect。每次扫描过程中,先等 CMSWaitDuration 时间(默认2秒),然后再判断是否满足 background collector 的触发条件。background collector 的触发条件:并行 Full GC,如调用了 System.gc()未配置 UseCMSInitiatingOccupancyOnly 时,会根据统计数据动态判断是否需要进行一次 CMS GC。如果预测 CMS GC 完成所需要的时间大于预计的老年代将要填满的时间,则进行 GC。这些判断是需要基于历史的 CMS GC 统计指标,第一次 CMS GC 时,统计数据还没有形成,是无效的,这时会跟据 Old Gen 的使用占比来判断是否要进行 GC。未配置 UseCMSInitiatingOccupancyOnly 时,判断 CMS 的使用率大于 CMSBootstrapOccupancy(默认50%)时触发 Old GC。老年代内存使用率阀值超过 CMSInitiatingOccupancyFraction(默认为92%)时触发 OldGC,CMSInitiatingOccupancyFraction 默认值为 -1,没有配置时默认阀值为 92%。未配置 UseCMSInitiatingOccupancyOnly 时,因为分配对象时内存不足导致的扩容等触发GC③ CMS参数设置在没有配置 UseCMSInitiatingOccupancyOnly 参数的情况下,会多出很多种触发可能,一般在生产环境会配置 UseCMSInitiatingOccupancyOnly 参数,配了之后就不用设置 CMSBootstrapOccupancy 参数了。CMSInitiatingOccupancyFraction 设置得太高将会很容易导致频繁的并发失败,性能反而降低;太低又可能频繁触发CMS background collector,一般在生产环境中应根据实际应用情况来权衡设置。-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSBootstrapOccupancy=92 -xx:CMSWaitDuration=20003)CMS 的问题:① 并发回收导致CPU资源紧张:在并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程而导致应用程序变慢,降低程序总吞吐量。CMS默认启动的回收线程数是:(CPU核数 + 3)/ 4,当CPU核数不足四个时,CMS对用户程序的影响就可能变得很大。② 无法清理浮动垃圾:在CMS的并发标记和并发清理阶段,用户线程还在继续运行,就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们,只好留到下一次垃圾收集时再清理掉。这一部分垃圾称为“浮动垃圾”。③ 并发失败(Concurrent Mode Failure):由于在垃圾回收阶段用户线程还在并发运行,那就还需要预留足够的内存空间提供给用户线程使用,因此CMS不能像其他回收器那样等到老年代几乎完全被填满了再进行回收,必须预留一部分空间供并发回收时的程序运行使用。默认情况下,当老年代使用了 92% 的空间后就会触发 CMS 垃圾回收,这个值可以通过 -XX: CMSInitiatingOccupancyFraction 参数来设置。这里会有一个风险:要是CMS运行期间预留的内存无法满足程序分配新对象的需要,就会出现一次“并发失败”(Concurrent Mode Failure),这时候虚拟机将不得不启动后备预案:Stop The World,临时启用 Serial Old 来重新进行老年代的垃圾回收,这样一来停顿时间就很长了。④ 内存碎片问题:CMS是一款基于“标记-清除”算法实现的回收器,这意味着回收结束时会有内存碎片产生。内存碎片过多时,将会给大对象分配带来麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不提前触发一次 Full GC 的情况。为了解决这个问题,CMS收集器提供了一个 -XX:+UseCMSCompactAtFullCollection 开关参数(默认开启),用于在 Full GC 时开启内存碎片的合并整理过程,由于这个内存整理必须移动存活对象,是无法并发的,这样停顿时间就会变长。还有另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数的作用是要求CMS在执行过若干次不整理空间的 Full GC 之后,下一次进入 Full GC 前会先进行碎片整理(默认值为0,表示每次进入 Full GC 时都进行碎片整理)。8、G1 垃圾回收器G1(Garbage First)回收器采用面向局部收集的设计思路和基于Region的内存布局形式,是一款主要面向服务端应用的垃圾回收器。G1设计初衷就是替换 CMS,成为一种全功能收集器。G1 在JDK9 之后成为服务端模式下的默认垃圾回收器,取代了 Parallel Scavenge 加 Parallel Old 的默认组合,而 CMS 被声明为不推荐使用的垃圾回收器。G1从整体来看是基于 标记-整理 算法实现的回收器,但从局部(两个Region之间)上看又是基于 标记-复制 算法实现的。1)可预期的回收停顿时间G1 可以指定垃圾回收的停顿时间,通过 -XX: MaxGCPauseMillis 参数指定,默认为 200 毫秒。这个值不宜设置过低,否则会导致每次回收只占堆内存很小的一部分,回收器的回收速度逐渐赶不上对象分配速度,导致垃圾慢慢堆积,最终占满堆内存导致 Full GC 反而降低性能。G1之所以能建立可预测的停顿时间模型,是因为它将 Region 作为单次回收的最小单元,即每次回收到的内存空间都是 Region 大小的整数倍,这样可以有计划地避免在整个Java堆中进行全区域的垃圾回收。G1会去跟踪各个Region的垃圾回收价值,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的回收停顿时间,优先处理回收价值收益最大的那些Region。这种使用Region划分内存空间,以及具有优先级的区域回收方式,保证了G1回收器在有限的时间内得到尽可能高的回收效率。由于Region数量比传统回收器的分代数量明显要多得多,因此G1回收器要比其他的传统垃圾回收器有着更高的内存占用负担。G1至少要耗费大约相当于Java堆容量10%至20%的额外内存来维持回收器工作。2)G1内存布局G1不再是固定大小以及固定数量的分代区域划分,而是把堆划分为多个大小相等的Region,每个Region的大小默认情况下是堆内存大小除以2048,因为JVM最多可以有2048个Region,而且每个Region的大小必须是2的N次冥。每个Region的大小也可以通过参数 -XX:G1HeapRegionSize 设定,取值范围为1MB~32MB,且应为2的N次幂。G1也有新生代和老年代的概念,不过是逻辑上的区分,每一个 Region 都可以根据需要,作为新生代的Eden空间、Survivor空间,或者老年代空间。新生代默认占堆内存的5%,但最多不超过60%,这个默认值可以使用 -XX:G1NewSizePercent 参数设置,最大值可以通过 -XX:G1MaxNewSizePercent 参数设置。新生代 Region 的数量并不是固定的,随着使用和垃圾回收会动态的变化。同样的,G1新生代也有 eden 区和 survivor 区的划分,也可以通过 -XX:SurvivorRatio 设置其比例,默认为8。3)大对象RegionRegion中还有一类特殊的 Humongous 区域,专门用来存储大对象,而不是直接进入老年代的Region。G1认为一个对象只要大小超过了一个Region容量的一半就判定为大对象。而对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的 Humongous Region 之中,G1的大多数行为都把 Humongous Region 作为老年代的一部分来看待。4)G1 回收过程G1 回收器的运作过程大致可分为四个步骤:1)初始标记(会STW):仅仅只是标记一下 GC Roots 能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。2)并发标记:从 GC Roots 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理在并发时有引用变动的对象。3)最终标记(会STW):对用户线程做短暂的暂停,处理并发阶段结束后仍有引用变动的对象。4)清理阶段(会STW):更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,必须暂停用户线程,由多条回收器线程并行完成的。5)G1新生代回收根据G1的内存布局举个例子,例如:设置堆内存 4G,就是 4096M,除以2048个Region,每个Region就是2M;新生代期初占5%,就是约100个Region,此时eden区占80个Region,两个survivor区各占10个Region;不过随着对象的在新生代分配,属于新生代的Region会不断增加,eden和survivor对应的Region也会不断增加。直到新生代占用60%,也就是约1200个Region,就会触发新生代的GC,这个时候就会采用复制算法将eden对应Region存活的对象复制到 from survivor 对应的Region。只不过这里会根据用户期望的停顿时间来选取部分最有回收价值的Region进行回收。6)G1混合回收G1有一个参数,-XX:InitiatingHeapOccupancyPercent,它的默认值是45%,就是如果老年代占堆内存45%的Region的时候,此时就会触发一次年轻代+老年代的混合回收。混合回收阶段,因为我们设定了最大停顿时间,所以 G1 会从新生代、老年代、大对象里挑选一些 Region,保证指定的时间内回收尽可能多的垃圾。所以 G1 可能一次无法将所有Region回收完,它就会执行多次混合回收,先停止程序,执行一次混合回收回收掉一些Region,接着恢复系统运行,然后再次停止系统运行,再执行一次混合回收回收掉一些Region。可以通过参数 -XX:G1MixedGCCountTarget 设置一次回收的过程中,最后一个阶段最多执行几次混合回收,默认值是8次。通过这种反复回收的方式,避免系统长时间的停顿。G1还有一个参数 -XX:G1HeapWastePercent,默认值是 5%。就是在混合回收时,Region回收后,就会不断的有新的Region空出来,一旦空闲出来的Region数量超过堆内存的5%,就会立即停止混合回收,即本次混合回收就结束了。G1还有一个参数 -XX:G1MixedGCLiveThresholdPercent,默认值是85%。意思是回收Region的时候,必须存活对象低于Region大小的85%时才可以进行回收,一个Region存活对象超过85%,就不必回收它了,因为要复制大部分存活对象到别的Region,这个成本是比较高的。7)回收失败① 并发回收失败在并发标记阶段,用户线程还在并发运行,程序继续运行就会持续有新对象产生,也需要预留足够的空间提供给用户线程使用。G1为每一个Region设计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须要在这两个指针位置以上。G1默认在这个地址以上的对象是被隐式标记过的,即默认它们是存活的,不纳入回收范围。如果内存回收的速度赶不上内存分配的速度,跟CMS会发生并发失败一样,G1也要被迫暂停程序,导致 Full GC 而产生长时间 Stop The World。② 混合回收失败混合回收阶段,年轻代和老年代都是基于复制算法进行回收,复制的过程中如果没有空闲的Region了,就会触发失败。一旦失败,就会停止程序,然后采用单线程标记、清理和内存碎片整理,然后空闲出来一批Region。这个过程是很慢的,因此要尽量调优避免混合回收失败的发生。9、总结对比1)垃圾回收器间的配合使用2)各个垃圾回收器对比10、GC性能衡量指标一个垃圾收集器在不同场景下表现出的性能也不一样,我们可以借助下面的一些指标来衡量GC的性能。1)吞吐量吞吐量是指应用程序所花费的时间和系统总运行时间的比值。系统总运行时间 = 应用程序耗时 +GC 耗时。如果系统运行了 100 分钟,GC 耗时 1 分钟,则系统吞吐量为 99%。GC 的吞吐量一般不能低于 95%。2)停顿时间指垃圾收集器正在运行时,应用程序的暂停时间。对于串行回收器而言,停顿时间可能会比较长;而使用并发回收器,由于垃圾收集器和应用程序交替运行,程序的停顿时间就会变短,但其效率很可能不如独占垃圾收集器,系统的吞吐量也很可能会降低。3)垃圾回收频率通常垃圾回收的频率越低越好,增大堆内存空间可以有效降低垃圾回收发生的频率,但同时也意味着堆积的回收对象越多,最终也会增加回收时的停顿时间。所以我们只要适当地增大堆内存空间,保证正常的垃圾回收频率即可。
使用linux肯定是要会使用命令的,就算提供有用户界面,绝大部分功能还是要通过命令行去操作的。而shell脚本语言也是运行在linux上的脚本语言,对于服务器运维人员也是几乎必须要掌握的。而shell脚本语言和linux命令有什么联系,有什么区别,也是要弄清楚的。什么是shellshell翻译成壳的意思,它是包裹在linux内核外层的、一个可通过一系列的linux命令对操作系统发出相关指令的人机界面。 shell可以通过其条件语句和循环语句等,把一系列linux命令结合在一起,形成一个相当于面向过程的程序,即shell script,来实现一些较为复杂的功能。shell与linux命令的联系shell是linux命令集的概称,是属于命令行的人机界面。shell 是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Ken Thompson的sh是第一种Unix Shell,Windows Explorer是一个典型的图形界面Shell。Shell 和Shell Script不是一回事,通常我们说的都是Shell Script。由定义可知, shell相当于经过装饰的命令行,和命令行一样,都能操作linux。但是shell是面向过程的,相当于有了一定的逻辑和过程,而命令行只是单一的操作。shell与linux命令的区别1.直接在命令行执行,就是在当前的shell环境下执行,比如涉及到一些环境变量的时候,必须在当前shell环境里执行。2.在脚本里执行的话,脚本执行的时候,会fork一个子进程,所有操作都在子进程进行。如果涉及到一些在脚本里设置环境变量的东西,脚本结束了,环境变量也就消失了。 如果是修改环境变量的话,需要特别注意下。3.shell可以重复或批量地执行一些命令。你也可以把自己要重复执行的命令写到脚本里面执行。而命令行的话就需要一个一个的输入命令,相对而言麻烦一些。总结上面就是一些对于shell和命令行的对比。个人觉得除了非常专业的运维人员,对大多数开发人员来说,这两者的差别不大,也不影响大家对于linux系统的使用。
一、Linux 简介Linux 内核最初只是由芬兰人李纳斯·托瓦兹(Linus Torvalds)在大学上学时出于个人爱好而编写的。Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。Linux 能运行主要的 UNIX 工具软件、应用程序和网络协议。它支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。二、Linux和UNIX的关系及区别Linux 和 UNIX 之间的关系是一个很有意思的话题。在目前主流的服务器端操作系统中,UNIX 诞生于 20 世纪 60 年代末,Windows 诞生于 20 世纪 80 年代中期,Linux 诞生于 20 世纪 90 年代初,可以说 UNIX 是操作系统中的"老大哥",后来的 Windows 和 Linux 都参考了 UNIX。UNIX 的发展历史UNIX 操作系统由肯•汤普森(Ken Thompson)和丹尼斯•里奇(Dennis Ritchie)发明。它的部分技术来源可追溯到从 1965 年开始的 Multics 工程计划,该计划由贝尔实验室、美国麻省理工学院和通用电气公司联合发起,目标是开发一种交互式的、具有多道程序处理能力的分时操作系统,以取代当时广泛使用的批处理操作系统。可惜,由于 Multics 工程计划所追求的目标太庞大、太复杂,以至于它的开发人员都不知道要做成什么样子,最终以失败收场。以肯•汤普森为首的贝尔实验室研究人员吸取了 Multics 工程计划失败的经验教训,于 1969 年实现了一种分时操作系统的雏形,1970 年该系统正式取名为 UNIX。随后出现了各种版本的 UNIX 系统,目前常见的有 Sun Solaris、FreeBSD、IBM AIX、HP-UX 等。它的商标权由国际开放标准组织所拥有,只有符合单一UNIX规范的UNIX系统才能使用UNIX这个名称,否则只能称为类UNIX。Linux 和 UNIX 的关系/区别Linux 是一个类似 Unix 的操作系统,Unix 要早于 Linux,Linux 的初衷就是要替代 UNIX,并在功能和用户体验上进行优化,所以 Linux 改进了 UNIX(但并没有抄袭 UNIX 的源码),使得 Linux 在外观和交互上与 UNIX 非常类似。正是由于 Linux 和 UNIX 有着千丝万缕的联系,所以人们把 Linux 叫做类UNIX系统。二者也有两个大的区别:UNIX 系统大多是与硬件配套的,也就是说,大多数 UNIX 系统如 AIX、HP-UX 等是无法安装在 x86 服务器和个人计算机上的,而 Linux 则可以运行在多种硬件平台上;UNIX 是商业软件,而 Linux 是开源软件,是免费、公开源代码的。三、Linux 的发行版介绍从技术上来说,李纳斯•托瓦兹开发的 Linux 只是一个内核。内核指的是一个提供设备驱动、文件系统、进程管理、网络通信等功能的系统软件,内核并不是一套完整的操作系统,它只是操作系统的核心。一些组织或厂商将 Linux 内核与各种软件和文档包装起来,并提供系统安装界面和系统配置、设定与管理工具,就构成了 Linux 的发行版本。Linux 的发行版说简单点就是将 Linux 内核与应用软件做一个打包。目前市面上较知名的发行版有:Ubuntu、RedHat、CentOS、Debian、Fedora、SuSE、OpenSUSE、Arch Linux、SolusOS 等。Linux各发行版简介Linux发行版主要有三个分支:Debian、Redhat、Slackware。Debian:(以社区的方式运作)Ubuntu: 基于知名的Debian Linux发展而来,界面友好,容易上手,对硬件的支持非常全面,是目前最适合做桌面系统的Linux发行版本,而且Ubuntu的所有发行版本都免费提供。Linux Mint: 基于Debian和Ubuntu的Linux发行版,致力于桌面系统对个人用户每天的工作更易用,更高效,且目标是提供一种更完整的即刻可用体验。Redhat:(商业公司维护的发行版本)RedHatLinux是Red Hat公司最早发行的个人版本的Linux,自从2013年RedHat9.0版本发布后,Red Hat公司停止了开发。RHEL(red hat enterprise Linux): Red Hat公司发布的面向企业用户的Linux操作系统,主要用在服务器上面,是收费版本。Fedora: 基于Red Hat Linux终止发行后,Red Hat公司计划以Fedora来取代Red Hat Linux在个人领域的应用,是免费版本。Centos: 是一款企业级Linux发行版,它使用Red hat Linux中的免费源代码重新构建而成,相当于RHEL的免费社区克隆版。slackwaresuse: 基于Slackware二次开发的一款Linux,主要用于商业桌面、服务器。SLES(SUSE Linux Enterprise Server): 企业服务器操作系统,是唯一与微软系统兼容的Linux操作系统。OpenSUSE: 由suse发展而来,旨在推进linux的广泛使用,主要用于桌面环境,用户界面非常华丽,而且性能良好。其他发行版本Gentoo: 基于linux的自由操作系统,它能为几乎任何应用程序或需求自动地作出优化和定制。由于它近乎无限的适应性,可把Gentoo称作元发行版。Aech Linux(Aech Linux: 以轻量简洁为设计理念的Linux发行版。其开发团队秉承简洁、优雅和代码最小化的设计宗旨。红旗Linux: 是由北京中科红旗软件技术有限公司开发的一系列Linux发行版,包括桌面版、工作站版、数据中心服务器版、HA集群版和红旗嵌入式Linux等产品,是中国较大、较成熟的Linux发行版之一。Linux发行版选择(1)想选择一个桌面系统,并且既不想用盗版,又不想花太多钱购买商业系统软件,那么可以选择Ubuntu桌面系统;(2)如果需要服务端的Linux系统,想用一个比较稳定的服务器系统,建议选择CentOS或者RHEL(Red Hat)。在这两者当中又首选CentOS,因为它是国内互联网公司使用的首选,如果公司有钱且不在乎软件成本则可以选择RHEL,RHEL有版权有售后,CentOS则无版权无售后;(3)如果对系统稳定性、安全性有更高的要求或者是特殊使用偏好的用户,可以考虑Debin或FreeBSD;(4)如果特别痴迷于新技术体验和追求最新的软件版本,可以选择Fedora,但要容忍Fedora潜在的新技术软件的BUG和系统稳定性的问题;(5)如果喜欢更好的中文环境支持,可以选择麒麟Linux和红旗Linux。
简介今天我将会给大家介绍如何在netty中使用UDP协议。UDP协议UDP( User Datagram Protocol ),也叫用户数据报协议。UDP 的主要功能和亮点并不在于它引入了什么特性,而在于它忽略的那些特性:不保证消息交付,不保证交付顺序,不跟踪连接状态,不需要拥塞控制。我们来看一下UDP的数据包:UDP是一种无连接的协议,发送者只管发送数据包即可,并不负责处理和保证数据是否成功发送,数据是否被处理完成等。它的唯一作用就是发送。在JDK中表示UDP的有一个专门的类叫做:java.net.DatagramPacket,在NIO中还有一个java.nio.channels.DatagramChannel,专门负责处理UDP的channel。这里我们要将的是netty,netty中对于UDP协议也有上面的两个类,名字虽然是一样的,但是对应的包不同。他们分别是:io.netty.channel.socket.DatagramPacket 和 io.netty.channel.socket.DatagramChannel,当然netty中的这两个类是对JDK自带类的增强。先看一下netty中DatagramPacket的定义:public class DatagramPacket extends DefaultAddressedEnvelope<ByteBuf, InetSocketAddress> implements ByteBufHolder DatagramPacket类实现了ByteBufHolder接口,表示它里面存放的是ByteBuf。然后他又继承自DefaultAddressedEnvelope,这个类是对地址的封装,其中ByteBuf表示传递消息的类型,InetSocketAddress表示目标的地址,它是一个IP地址+端口号的封装。从上面的UDP协议我们知道,UDP只需要知道目标地址和对应的消息即可,所以DatagramPacket中包含的数据已经够用了。DatagramChannel是用来传递DatagramPacket的,因为DatagramChannel是一个接口,所以一般使用NioDatagramChannel作为真正使用的类。String和ByteBuf的转换之前我们讲到过,netty中的channel只接受ByteBuf数据类型,如果直接写入String会报错,之前的系列文章中,我们讲过两种处理方法,第一种是使用ObjectEncoder和ObjectDecoder在写入ByteBuf之前,对对象进行序列化,这一种不仅适合String,也适合Object对象。第二种是使用StringEncoder和StringDecoder专门处理String的encode和decode,这种处理只能处理String的转换,对Object无效。如果你不想使用这些encoder和decoder还可以直接使用ByteBuf和String进行转换。比如要将String写入ByteBuf可以调用Unpooled.copiedBuffer的命令如下:Unpooled.copiedBuffer("开始广播", CharsetUtil.UTF_8)将ByteBuf转换成为String则可以调用:byteBuf.toString(CharsetUtil.UTF_8)构建DatagramPacketDatagramPacket总共可以接受三个参数,分别是要发送的数据data,要接收数据包的地址和要发送数据包的地址。这里我们并不关心发送数据包的地址,那么只需要两个参数即可,对于客户端来说,我们发送一个”开始广播“的消息给服务器端,告诉服务器端可以向客户发送回复消息了,如下所示:new DatagramPacket( Unpooled.copiedBuffer("开始广播", CharsetUtil.UTF_8), SocketUtils.socketAddress("255.255.255.255", PORT))上我们使用SocketUtils.socketAddress创建了一个特殊的地址,255.255.255.255是一个特殊的广播地址,意味着所有的主机,因为我们的客户端并不知道服务器的地址,所以使用255.255.255.255来广播。构建好的DatagramPacket,里面有一个sender()方法,可以用来获取客户端的地址,所以在服务器端可以这样构建要发送的packge:new DatagramPacket( Unpooled.copiedBuffer("广播: " + nextQuote(), CharsetUtil.UTF_8), packet.sender())启动客户端和服务器UDP的客户端和服务器启动和socket稍微有所不同,如果是socket,那么使用的channel是NioSocketChannel,如果是UDP,则使用的是NioDatagramChannel。如下是服务器端启动的代码:EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .handler(new UDPServerHandler()); b.bind(PORT).sync().channel().closeFuture().await(); } finally { group.shutdownGracefully(); }注意,这里我们需要设置ChannelOption.SO_BROADCAST为true,因为UDP是以广播的形式发送消息的。客户端的实现和socket稍微有所不同,下面是客户端的启动实现: EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .handler(new UDPClientHandler()); Channel ch = b.bind(0).sync().channel();对于UDP来说,并不存在地址绑定一说,所以上Bootstrap调用bind(0)。总结本文讲解了netty中UDP协议的实现,UDP相较于Socket连接而言更加简单。
简介我们常用浏览器来访问web页面得到相关的信息,通常来说使用的都是HTTP或者HTTPS协议,这些协议的本质上都是IO,客户端的请求就是In,服务器的返回就是Out。但是在目前的协议框架中,并不能完全满足我们所有的需求。比如使用HTTP下载大文件,可能需要长连接等待。我们也知道IO方式有多种多样的,包括同步IO,异步IO,阻塞IO和非阻塞IO等。不同的IO方式其性能也是不同的,而netty就是一个基于异步事件驱动的NIO框架。本系列文章将会探讨netty的详细使用,通过原理+例子的具体结合,让大家了解和认识netty的魅力。netty介绍netty是一个优秀的NIO框架,大家对IO的第一印像应该是比较复杂,尤其是跟各种HTTP、TCP、UDP协议打交道,使用起来非常复杂。但是netty提供了对这些协议的友好封装,通过netty可以快速而且简洁的进行IO编程。netty易于开发、性能优秀,同时兼具稳定性和灵活性。如果你希望开发高性能的服务,那么使用netty总是没错的。netty的最新版本是4.1.66.Final,事实上这个版本是官方推荐的最稳定的版本,netty还有5.x的版本,但是官方并不推荐。如果要在maven项目中使用,可以引入下面的依赖坐标: <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.66.Final</version> </dependency>下面我们将会从一个最简单的例子,体验netty的魅力。netty的第一个服务器什么叫做服务器?能够对外提供服务的程序就可以被称作是服务器。建立服务器是所有对外服务的第一步,怎么使用netty建立一个服务器呢?服务器主要负责处理各种服务端的请求,netty提供了一个ChannelInboundHandlerAdapter的类来处理这类请求,我们只需要继承这个类即可。在NIO中每个channel都是客户端和服务器端沟通的通道。ChannelInboundHandlerAdapter定义了在这个channel上可能出现一些事件和情况,如下图所示:如上图所示,channel上可以出现很多事件,比如建立连接,关闭连接,读取数据,读取完成,注册,取消注册等。这些方法都是可以被重写的,我们只需要新建一个类,继承ChannelInboundHandlerAdapter即可。这里我们新建一个FirstServerHandler类,并重写channelRead和exceptionCaught两个方法,第一个方法是从channel中读取消息,第二个方法是对异常进行处理。public class FirstServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { // 对消息进行处理 ByteBuf in = (ByteBuf) msg; try { log.info("收到消息:{}",in.toString(io.netty.util.CharsetUtil.US_ASCII)); }finally { ReferenceCountUtil.release(msg); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 异常处理 log.error("出现异常",cause); ctx.close(); } }上面例子中,我们收到消息后调用release()方法将其释放,并不进行实际的处理。调用release方法是在消息使用完成之后常用的做法。上面代码将msg进行了ByteBuf的强制转换,如果并不想进行转换的话,可以直接这样使用: try { // 消息处理 } finally { ReferenceCountUtil.release(msg); }在异常处理方法中,我们打印出异常信息,并关闭异常的上下文。有了Handler,我们需要新建一个Server类用来使用Handler创建channel和接收消息。接下来我们看一下netty的消息处理流程。在netty中,对IO进行处理是使用多线程的event loop来实现的。netty中的EventLoopGroup就是这些event loop的抽象类。我们来观察一下EventLoopGroup的类结构。可以看出EventLoopGroup继承自EventExecutorGroup,而EventExecutorGroup继承自JDK自带的ScheduledExecutorService。所以EventLoopGroup本质是是一个线程池服务,之所以叫做Group,是因为它里面包含了很多个EventLoop,可以通过调用next方法对EventLoop进行遍历。EventLoop是用来处理注册到该EventLoop的channel中的IO信息,一个EventLoop就是一个Executor,通过不断的提交任务进行执行。当然,一个EventLoop可以注册多个channel,不过一般情况下并不这样处理。EventLoopGroup将多个EventLoop组成了一个Group,通过其中的next方法,可以对Group中的EventLoop进行遍历。另外EventLoopGroup提供了一些register方法,将Channel注册到当前的EventLoop中。从上图可以看到,register的返回结果是一个ChannelFuture,Future大家都很清楚,可以用来获得异步任务的执行结果,同样的ChannelFuture也是一个异步的结果承载器,可以通过调用sync方法来阻塞Future直到获得执行结果。可以看到,register方法还可以传入一个ChannelPromise对象,ChannelPromise它同时是ChannelFuture和Promise的子类,Promise又是Future的子类,它是一个特殊的可以控制Future状态的Future。EventLoopGroup有很多子类的实现,这里我们使用NioEventLoopGroup,Nio使用Selector对channel进行选择。还有一个特性是NioEventLoopGroup可以添加子EventLoopGroup。对于NIO服务器程序来说,我们需要两个Group,一个group叫做bossGroup,主要用来监控连接,一个group叫做worker group,用来处理被boss accept的连接,这些连接需要被注册到worker group中才能进行处理。将这两个group传给ServerBootstrap,就可以从ServerBootstrap启动服务了,相应的代码如下://建立两个EventloopGroup用来处理连接和消息 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new FirstServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); // 绑定端口并开始接收连接 ChannelFuture f = b.bind(port).sync();我们最开始创建的FirstServerHandler最作为childHandler的处理器在初始化Channel的时候就被添加进去了。这样,当有新建立的channel时,FirstServerHandler就会被用来处理该channel的数据。上例中,我们还指定了一些ChannelOption,用于对channel的一些属性进行设定。最后,我们绑定了对应的端口,并启动服务器。netty的第一个客户端上面我们已经写好了服务器,并将其启动,现在还需要一个客户端和其进行交互。如果不想写代码的话,可以直接telnet localhost 8000和server端进行交互即可,但是这里我们希望使用netty的API来构建一个client和Server进行交互。构建netty客户端的流程和构建netty server端的流程基本一致。首先也需要创建一个Handler用来处理具体的消息,同样,这里我们也继承ChannelInboundHandlerAdapter。上一节讲到了ChannelInboundHandlerAdapter里面有很多方法,可以根据自己业务的需要进行重写,这里我们希望当Channel active的时候向server发送一个消息。那么就需要重写channelActive方法,同时也希望对异常进行一些处理,所以还需要重写exceptionCaught方法。如果你想在channel读取消息的时候进行处理,那么可以重写channelRead方法。创建的FirstClientHandler代码如下:@Slf4j public class FirstClientHandler extends ChannelInboundHandlerAdapter { private ByteBuf content; private ChannelHandlerContext ctx; @Override public void channelActive(ChannelHandlerContext ctx) { this.ctx = ctx; content = ctx.alloc().directBuffer(256).writeBytes("Hello World".getBytes(StandardCharsets.UTF_8)); // 发送消息 sayHello(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 异常处理 log.error("出现异常",cause); ctx.close(); } private void sayHello() { // 向服务器输出消息 ctx.writeAndFlush(content.retain()); } }上面的代码中,我们首先从ChannelHandlerContext申请了一个ByteBuff,然后调用它的writeBytes方法,写入要传输的数据。最后调用ctx的writeAndFlush方法,向服务器输出消息。接下来就是启动客户端服务了,在服务端我们建了两个NioEventLoopGroup,是兼顾了channel的选择和channel中消息的读取两部分。对于客户端来说,并不存在这个问题,这里只需要一个NioEventLoopGroup即可。服务器端使用ServerBootstrap来启动服务,客户端使用的是Bootstrap,其启动的业务逻辑基本和服务器启动一致:EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new FirstClientHandler()); } }); // 连接服务器 ChannelFuture f = b.connect(HOST, PORT).sync();运行服务器和客户端有了上述的准备工作,我们就可以运行了。首先运行服务器,再运行客户端。如果没有问题的话,应该会输出下面的内容:[nioEventLoopGroup-3-1] INFO com.yyb.FirstServerHandler - 收到消息:Hello World总结一个完整的服务器、客户端的例子就完成了。我们总结一下netty的工作流程,对于服务器端,首先建立handler用于对消息的实际处理,然后使用ServerBootstrap对EventLoop进行分组,并绑定端口启动。对于客户端来说,同样需要建立handler对消息进行处理,然后调用Bootstrap对EventLoop进行分组,并绑定端口启动。有了上面的讨论就可以开发属于自己的NIO服务了。是不是很简单? 后续文章将会对netty的架构和背后的原理进行深入讨论,敬请期待。
一、前言Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单。与Spring Security对比,Shiro可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。下面对这两个安全框架进行了对比,可以根据你的项目需要选出适合的安全框架。二、Shiro架构与功能介绍1.认证与授权相关概念安全实体:系统需要保护的具体对象数据权限:系统相关的功能操作,例如基本的CRUDAuthentication:身份认证/登录,验证用户是不是拥有相应的身份;Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;Web Support:Web支持,可以非常容易的集成到Web环境;Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;Testing:提供测试支持;Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。2.Shiro四大核心功能Authentication,Authorization,Cryptography,Session Management3.Shiro三个核心组件Subject, SecurityManager 和 Realms.Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。三、Spring Security简介Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。它是一个轻量级的安全框架,它确保基于Spring的应用程序提供身份验证和授权支持。它与Spring MVC有很好地集成,并配备了流行的安全算法实现捆绑在一起。安全主要包括两个操作“认证”与“验证”(有时候也会叫做权限控制)。“认证”是为用户建立一个其声明的角色的过程,这个角色可以一个用户、一个设备或者一个系统。“验证”指的是一个用户在你的应用中能够执行某个操作。在到达授权判断之前,角色已经在身份认证过程中建立了。它的设计是基于框架内大范围的依赖的,可以被划分为以下几块。Web/Http 安全:这是最复杂的部分。通过建立 filter 和相关的 service bean 来实现框架的认证机制。当访问受保护的 URL 时会将用户引入登录界面或者是错误提示界面。业务对象或者方法的安全:控制方法访问权限的。AuthenticationManager:处理来自于框架其他部分的认证请求。AccessDecisionManager:为 Web 或方法的安全提供访问决策。会注册一个默认的,但是我们也可以通过普通 bean 注册的方式使用自定义的 AccessDecisionManager。AuthenticationProvider:AuthenticationManager 是通过它来认证用户的。UserDetailsService:跟 AuthenticationProvider 关系密切,用来获取用户信息的。四、Shiro和Spring Security比较Shiro比Spring更容易使用、实现和理解。Spring Security更加知名的唯一原因是因为品牌名称“Spring”以简单而闻名,但讽刺的是很多人发现使用Spring Security很困难。但是,Spring Security却有更好的社区支持。Apache Shiro与Spring Security处理密码学方面相比有一个额外的模块。Spring Security 与Spring 结合地较好,如果项目用的springmvc ,使用起来很方便。但是如果项目中没有用到spring框架,那就不要考虑它了。Shiro 功能强大、且简单、灵活。是Apache基金会下的项目,比较可靠,且不跟任何框架或者容器绑定,可以独立运行。
一、简介1、历史mybatis的前身是apache ibatis,2010年由ASF转移到google code,从ibatis3.x改名为mybatis,2013年转移到github。mybatis是由java实现的持久层框架。2、mybatis的特性mybatis是一个半自动化的持久层框架,可以注解配置、映射xml文件中编写动态sql语句。通常使用xml编写sql。mybatis是对jdbc封装的框架,几乎不需要jdbc操作mybatis提供面向接口编程,持久层只需要一个接口,不需要实现类。3、和其他持久层框架 的比较jdbc : 硬编码,在java语句中写sql,耦合度高hibernate、jpa:自动框架,由框架生成sql语句,不需要人为编写,灵活度较差,高度定制化的sql语句需要跳过框架。mybatis:半自动化的框架,可以注解配置、xml配置。二、映射文件1、mybatis的映射文件结构<!DOCTYPE mapper ...> <mapper namespace="com.sly.ecs.business.mapper.mission.MissionMapper"> <insert id="insertBatch" parameterType="list" ...> <select id="selectMission" parameterType="int" resultMap="BaseResultMap" ...> </mapper>通过顶级标签中的namespace属性来指定对应的mapper接口,通过子标签的id属性来指定对应的抽象方法。2、参数(1)传入参数的情形:①、mapper接口方法的参数是单个:xml文件用#{ },来取值,需要注意 { }来取值,需要注意来取值,需要注意{ }要用单引号。{}中的名称是任意的,因为只有一个参数,无论取值时叫什么,都能够取到值。②、mapper接口方法的参数是多个:会用Map集合保存参数,键是由mybatis框架自动生成1)xml文件可以用 arg0 、arg1 。。。 获取对应位置的参数2)xml文件可以用 param1 、param2 。。。 获取对应位置的参数也可以交叉使用,例如 arg0、 param2③、mapper接口方法的参数是多个,可以放到一个Map集合里键是由开发者人为设置。在xml映射文件里根据人为设置的键获取参数。例如:在600ly公司代码中使用 GenericQueryParam装载参数。public final class GenericQueryParam extends LinkedHashMap<String, Object> implements ListQueryParam { ... }var param = new GenericQueryParam(); param.fill("signCode", signCode).fill("typeId", typeId).fill("labelId", labelId) .fill("areaId", areaId).fill("status", status).fill("userId", userId);④、mapper接口方法的参数是实体类xml文件里根据实体类的属性获取属性值。补充知识:实体类中有set、get方法的才算做属性,不是按照全局变量来定义的。⑤、mapper接口方法传入@Param注解参数参数会保存到Map集合,以@Param的值作为键,xml文件以Map的键来获取参数。归类:①、参数加上@Param注解 传入方法②、参数装进Map或者实体类 传入方法(2)获取参数的两种方式:(jdbc原生写法)${} 字符串拼接。不会带上单引号。注意要手动加上单引号。#{} 占位符赋值。会带上单引号。不需要手动加上单引号。示例:①、常规username = #{username} AND sign_code = #{signCode}②、Mybatis处理模糊查询role_name LIKE CONCAT('%',#{roleName},'%') role_name LIKE '%${roleName}%' role_name LIKE "%"#{roleName}"%"③、in范围查询:String ids=“1,4,25”;pk_id IN (${ids})④、动态设置表名、字段名、排序关键字:select * from ${tableName} ORDER BY ${sortColumn} ${sortOrder}总结: sql语法中需要引号的 尽量使用#{},或者${} 手动加上引号。 例如字符串、日期等类型的值。 sql语法中不需要引号的 使用${} ,不用手动加上引号。例如in范围查询、表名、字段、排序关键字。3、返回值(1)增删改操作的返回值可以是void、intint类型的返回值是sql语句影响的数据条数。(2)查询操作的返回值在xml映射文件标签的属性resultType或resultMap里设置。resultType是单个类型,resultMap是人为设置的字段-属性的映射关系。select查询结果的情况:①、如果查询的结果是一条记录查询结果可以用实体类或者list 或者 map集合接收。用map集合接收,查询结果的字段作为键,字段对应的值作为值。②、如果查询的结果是多条记录1)可以用List<> 接收。List<实体类>或者 List<Map<String,Object>接收不能用单个实体类接收多条记录。会报TooManyResultsException。2)可以用一个map集合接收。在@MapKey(“”) 注解的value属性中指定某个字段的值作为map的键。例如:@MapKey(“id”)③、查询结果是单行单列,可以用基本数据或String类型接收。4、类型别名Mybatis框架对常用的类型设置了对应的别名,用来替代全类名,简化代码。简记:基本数据类型前面加下划线,复杂数据类型为全小写。5、添加操作,获取自增主键User u=new User(null,"yang","123456","男"); userMapper.insertUser(u); u.getId();int insertUser(User user);<insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> insert into t_user values(null,#{username},#{password},#{sex} ) </insert>useGeneratedKeys:设置当前的sql使用了自增的主键。keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性。6、解决字段名和属性名不一致的情况1)在Mapper.xml文件里给sql语句的字段起别名emp_name as empName2)在mybatis配置文件设置 下划线转换为驼峰式。<settings> <setting name="mapUnderscoresToCamelCase" value="true"/> </settings> 3)在Mapper.xml文件里设置结果映射<resultMap id="BaseResultMap" type="User" > <id property="pkId" colume="pk_id"/> <result property="userName" colume="user_name"/> <result property="password" colume="password"/> <result property="sex" colume="sex"/> <result property="email" colume="email"/> </resultMap>处理多对一 映射关系:例:dept部门表和emp员工表是一对多的关系。在Emp实体类中加入Dept dept属性。select * from t_emp t1 left join t_dept t2 on t1.did=t2.did需要将查询结果字段装到Emp类中。方式1:设置级联映射<resultMap id="empAndDeptMap" type="Emp" > <id property="eid" colume="eid"/> <result property="empName" colume="emp_name"/> <result property="address" colume="address"/> <result property="sex" colume="sex"/> <result property="email" colume="email"/> <result property="dept.did" colume="did"/> <result property="dept.deptName" colume="dept_name"/> </resultMap>方式2:association<resultMap id="empAndDeptMap" type="Emp" > <id property="eid" colume="eid"/> <result property="empName" colume="emp_name"/> <result property="address" colume="address"/> <result property="sex" colume="sex"/> <result property="email" colume="email"/> <association property="dept" javaType="Dept"> <id property="did" colume="did"/> <result property="deptName" colume="dept_name"/> </association> </resultMap>方法3:分步查询处理一对多 映射关系:三、动态sql1、where、 if 标签if 标签:对标签中test属性的表达式 进行判断,为true则显示标签内的过滤条件,为false则不显示标签内的过滤条件。where标签中 没有过滤条件,不加where关键字;有过滤条件,加上where关键字,并将第一个条件前边的and 或者or 关键字删掉。<select id="selectRoleByName" resultType="_int" parameterType="param"> SELECT * FROM t_project_role <where> <if test="roleName != null and roleName != ''"> AND role_name = #{roleName} </if> <if test="roleId != null and roleId != ''"> AND pk_id != #{roleId} </if> <if test="roleType != null and roleType != ''"> OR role_type = #{roleType} </if> </where> </select>如果 第2、3个if标签存在,则生成的动态sql是SELECT * FROM t_project_role where pk_id != #{roleId} OR role_type = #{roleType}2、trim 标签有四个属性。prefix,suffix:给trim标签里的内容加上前、后缀。prefixoverrides,suffixoverrides:给trim标签里的内容 前或后 去掉内容。<select id="selectRoleByName" resultType="_int" parameterType="param"> SELECT * FROM t_project_role <trim prefix="where" suffixoverrides="AND|OR"> <if test="roleName != null and roleName != ''"> role_name = #{roleName} AND </if> </trim> </select>生成的动态sql是SELECT * FROM t_project_role where role_name != #{roleName }3、choose、when、otherwise标签相当于java语言中if…else if…else 条件判断代码块。when 标签对test属性值进行判断,值为true则作为动态sql的条件,不再判断后面的条件。所以标签里的条件不需要加and或者or 。值为false则判断后面的条件。如果when标签中的test 属性值都为false,则将otherwise标签作为条件。when标签至少有一个,otherwise标签最多有一个。<select id="findUserByChoose" parameterType="domain.User" resultType="domain.User"> select * from USER <where> <choose> <when test="username!=null and username!=''"> username=#{username} </when> <when test="sex!=null and sex!=''"> sex=#{sex} </when> <otherwise> did=1 </otherwise> </choose> </where> </select>4、foreach 遍历集合、数组(1)in 范围查找DELETE FROM t_code WHERE pk_id IN <foreach collection="list" item="pkId" open="(" close=")" separator="," > #{pkId} </foreach>DELETE FROM t_code WHERE pk_id IN (12,23,55,86)(2)批量添加INSERT INTO t_user(pk_id, name, sex, age) values <foreach collection="list" item="user" separator=","> ( null, #{user.username}, #{user.sex}, #{user.age} ) </foreach> 、5、sql写一些常用的sql块例如:<sql id="Base_Column_List" > pk_id, code_class, code_name, sort, remark, create_user, create_date, update_user, update_date, is_valid </sql><sql id="Base_Count"> SELECT COUNT(0) FROM t_code </sql><sql id="Base_Select"> SELECT <include refid="Base_Column_List" /> FROM t_code </sql><sql id="Base_Where"> <where> <if test="pkId != null and pkId != ''"> AND pk_id = #{pkId} </if> <if test="codeClass != null and codeClass != ''"> AND code_class = #{codeClass} </if> <if test="codeName != null and codeName != ''"> AND code_name = #{codeName} </if> <if test="(sort != null and sort != '') or sort == 0"> AND sort = #{sort} </if> <if test="remark != null and remark != ''"> AND remark = #{remark} </if> <if test="(createUser != null and createUser != '') or createUser == 0"> AND create_user = #{createUser} </if> <if test="createDate != null"> AND create_date = #{createDate} </if> <if test="(updateUser != null and updateUser != '') or updateUser == 0"> AND update_user = #{updateUser} </if> <if test="updateDate != null"> AND update_date = #{updateDate} </if> <if test="(isValid != null and isValid != '') or isValid == 0"> AND is_valid = #{isValid} </if> </where> </sql><sql id="Base_Sort"> <if test="_parameter.getSortParam().size() > 0"> ORDER BY <foreach collection="_parameter.getSortParam()" item="sortParam" separator=","> ${sortParam.column} ${sortParam.order} </foreach> </if> </sql><sql id="Base_Page"> <if test="isPage == 1"> LIMIT #{_offset}, #{_pagesize} </if> </sql>四、mybatis缓存1、一级缓存使用同一个SqlSession,根据相同的查询条件,只在第一次查询时执行sql语句,之后每次查询,都使用mybatis缓存的结果数据。一级缓存失效的情形:(1)不同的SqlSession对应不同的一级缓存(2)同一个SqlSession ,查询条件不同(3)同一个SqlSession ,两次查询之间有增删改操作(4)同一个SqlSession ,两次查询之间清理过缓存clearCache()2、二级缓存使用同一个SqlSessionFactory,只在第一次查询时执行sql语句,之后每次查询,都是用mybatis二级缓存。二级缓存开启的条件:(1)在核心配置文件中加上cacheEnabled="true"全局属性(2)在映射文件加上 标签(3)在两次查询之间,对SqlSession 进行commit()或close()(4)查询结果转换的实体类型要实现序列化接口。失效的情形:在两次查询之间,执行任意的增删改操作,一级和二级缓存都会失效。3、mybatis缓存的查询顺序(1)先在二级缓存中查找(2)二级缓存没有,则在一级缓存中查找(3)一级缓存中没有,则到数据库中查找(4)SqlSession关闭之后,一级缓存中的数据保存到二级缓存中。mybatis是持久层框架,做缓存功能不是很擅长,有开放的接口可以使用第三方缓存工具。只能替代mybatis的二级缓存。例如EHCache。五、mybatis逆向工程正向工程:根据实体类 生成数据库表。例如Hibernate框架逆向工程:根据数据库表生成实体类、映射文件、mapper接口。mybatis的逆向工程分为简易版和全面版。简易版包括五个增删改查方法。其中查询方法包括查询全部和根据id查找。
一、kafka简介kafka是分布式的、基于发布/订阅模式的消息队列。a系统获取b系统的数据,不是直接获取,而是通过消息队列获取。作用:解耦,削峰填谷如果公司用spark作为数据分析框架,那么90%的概率使用kafka二、docker 安装kafka集群(一)安装docker-compose下载最新版的docker-compose文件 curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 添加可执行权限 chmod +x /usr/local/bin/docker-compose 测试安装结果 docker-compose --version >> docker-compose version 1.25.1, build a82fef07(二)拉取镜像docker pull zookeeper:3.4 docker pull wurstmeister/kafka:2.13-2.7.0 docker pull sheepkiller/kafka-manager:latest创建集群网络 docker network create --driver bridge --subnet 192.168.0.0/24 --gateway 192.168.0.1 kafka 查看 docker network ls网络设置:echo "net.ipv4.ip_forward=1" >>/usr/lib/sysctl.d/00-system.confsystemctl restart network && systemctl restart docker将三个yml文件保存到centos系统中的 /opt/file/kafka 目录里docker-compose -f /opt/file/kafka/docker-compose-zookeeper.yml up -d docker-compose -f /opt/file/kafka/docker-compose-kafka.yml up -d docker-compose -f /opt/file/kafka/docker-compose-manager.yml up -d浏览器访问宿主机:http://192.168.1.167:9000/
一、mqtt协议简介MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。1.1 MQTT协议特点MQTT是一个基于客户端-服务器的消息发布/订阅的传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。MQTT协议当前版本为,2014年发布的MQTT v3.1.1。除标准版外,还有一个简化版MQTT-SN,该协议主要针对嵌入式设备,这些设备一般工作于TCP/IP网络,如:ZigBee。MQTT 与 HTTP 一样,MQTT 运行在传输控制协议/互联网协议 (TCP/IP) 堆栈之上。1.2 发布和订阅MQTT使用的发布/订阅消息模式,它提供了一对多的消息分发机制,从而实现与应用程序的解耦。这是一种消息传递模式,消息不是直接从发送器发送到接收器(即点对点),而是由MQTT server(或称为 MQTT Broker)分发的。MQTT 服务器是发布-订阅架构的核心。它可以非常简单地在Raspberry Pi或NAS等单板计算机上实现,当然也可以在大型机或 Internet 服务器上实现。服务器分发消息,因此必须是发布者,但绝不是订阅者!客户端可以发布消息(发送方)、订阅消息(接收方)或两者兼而有之。客户端(也称为节点)是一种智能设备,如微控制器或具有 TCP/IP 堆栈和实现 MQTT 协议的软件的计算机。消息在允许过滤的主题下发布。主题是分层划分的 UTF-8 字符串。不同的主题级别用斜杠/作为分隔符号。我们来看看下面的设置。光伏发电站是发布者(Publisher)。主要主题(Topic)级别是"PV",这个工厂发布两个子级别"sunshine"和"data";"PV/sunshine"是一个布尔值(true/false,也可以是 1/0),充电站需要它来知道是否应该装载电动汽车(仅在阳光普照时 😃)。充电站(EVSE)是订阅者,订阅"PV/sunshine"从服务器获取信息。“PV/data” 另一方面,以 kW 为单位传输工厂产生的瞬时功率,并且该主题可以例如通过计算机或平板电脑订阅,以生成一天内传输功率的图表。这就是一个简单的MQTT的应用场景,具体如下图所示;MQTT 发布和订阅1.3 QoS(Quality of Service levels)服务质量是 MQTT 的一个重要特性。当我们使用 TCP/IP 时,连接已经在一定程度上受到保护。但是在无线网络中,中断和干扰很频繁,MQTT 在这里帮助避免信息丢失及其服务质量水平。这些级别在发布时使用。如果客户端发布到 MQTT 服务器,则客户端将是发送者,MQTT 服务器将是接收者。当MQTT服务器向客户端发布消息时,服务器是发送者,客户端是接收者。QoS 0这一级别会发生消息丢失或重复,消息发布依赖于底层TCP/IP网络。即:<=1QoS 1QoS 1 承诺消息将至少传送一次给订阅者。QoS 2使用 QoS 2,我们保证消息仅传送到目的地一次。为此,带有唯一消息 ID 的消息会存储两次,首先来自发送者,然后是接收者。QoS 级别 2 在网络中具有最高的开销,因为在发送方和接收方之间需要两个流。二、MQTT 数据包结构固定头(Fixed header),存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识;可变头(Variable header),存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容;消息体(Payload),存在于部分MQTT数据包中,表示客户端收到的具体内容;整体MQTT的消息格式如下图所示;2.1 MQTT固定头 ( FixedHeader )固定头存在于所有MQTT数据包中,其结构如下:下面简单分析一下固定头的消息格式;MQTT消息类型 / message type**位置:**byte 1, bits 7-4。4位的无符号值,类型如下:标识位 / DUP**位置:**byte 1, bits 3-0。在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接。DUP:发布消息的副本。用来在保证消息的可靠传输,如果设置为 1,则在下面的变长中增加MessageId,并且需要回复确认,以保证消息传输完成,但不能用于检测消息重复发送。QoS发布消息的服务质量(前面已经做过介绍),即:保证消息传递的次数00:最多一次,即:<=101:至少一次,即:>=110:一次,即:=111:预留RETAIN:发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放。剩余长度(Remaining Length)位置:byte 1。固定头的第二字节用来保存变长头部和消息体的总大小的,但不是直接保存的。这一字节是可以扩展,其保存机制,前7位用于保存长度,后一部用做标识。当最后一位为 1时,表示长度不足,需要使用二个字节继续保存。例如:计算出后面的大小为02.2 MQTT可变头 ( Variable header )MQTT数据包中包含一个可变头,它驻位于固定的头和负载之间。可变头的内容因数据包类型而不同,较常的应用是做为包的标识:Bit7 — 0byte 1包标签符(MSBbyte 2…包标签符(LSB)很多类型数据包中都包括一个2字节的数据包标识字段,这些类型的包有:PUBLISH (QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK2.3 消息体 ( Payload )Payload消息体是MQTT数据包的第三部分,CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息 有消息体:CONNECT,消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码SUBSCRIBE,消息体内容是一系列的要订阅的主题以及QoS。SUBACK,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。UNSUBSCRIBE,消息体内容是要订阅的主题。
TCP/IP协议是一种能够在多个不同网络间实现信息传输的协议簇,其中应用层的主要协议有Telnet、FTP、SMTP、HTTP等,是用来接收来自传输层的数据或者按不同应用要求与方式将数据传输至传输层。一、UDP1、DNS:域名系统DNS是因特网使用的命名系统,用来把人们使用的机器名字转换为IP地址。现在顶级域名TLD分为三大类:国家顶级域名nTLD;通用顶级域名gTLD;基础结构域名域名服务器分为四种类型:根域名服务器;顶级域名服务器;本地域名服务器;权限域名服务器。2、SNMP:简单网络管理协议SNMP由三部分组成:SNMP本身、管理信息结构SMI和管理信息库MIB。SNMP定义了网络管理站(NMS)和代理(Agent)之间所交换的分组格式。SMI定义了命名对象类型的通用规则,以及把对象和对象的值进行编码。MIB在被管理的实体中创建了命名对象,并规定类型。3、TFTP:简单文件传送协议TFTP是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。TFTP是一个传输文件的简单协议,它基于UDP协议而实现,但是我们也不能确定有些TFTP协议是基于其它传输协议完成的。此协议设计的时候是进行小文件传输的。因此它不具备通常的FTP的许多功能,它只能从文件服务器上获得或写入文件,不能列出目录,不进行认证,它传输8位数据。传输中有三种模式:netascii,这是8位的ASCII码形式,另一种是octet,这是8位源数据类型;最后一种mail已经不再支持,它将返回的数据直接返回给用户而不是保存为文件。特点:数据报文都是512字节(最后一次可≤512Bytes)数据报文按序号编号,从1开始支持ASCII码和二进制传送(FTP也支持)可以对文件进行读写首部简单二、TCP1、FTP:文件传输协议FTP是因特网上使用得最广泛的文件传送协议。FTP提供交互式的访问,允许客户指明文件类型与格式,并允许文件具有存取权限。FTP基于TCP。SFTP是FTP的安全版本。 SFTP提供与远程服务器的加密通道。 这使身份验证和数据传输比FTP更安全。2、HTTP(Hypertext Transfer Protocol ):超文本传输协议HTTP是面向事务的应用层协议,它是万维网上能够可靠地交换文件的重要基础。http使用面向连接的TCP作为运输层协议,保证了数据的可靠传输。3、HTTPS(Hypertext Transfer Protocol Secure):安全超文本传输协议它是由Netscape开发并内置于其浏览器中,用于对数据进行压缩和解压操作,并返回网络上传送回的结果。HTTPS实际上应用了Netscape的安全套接字层(SSL)作为HTTP应用层的子层。(HTTPS使用端口443,而不是像HTTP那样使用端口80来和TCP/IP进行通信。)SSL使用40 位关键字作为RC4流加密算法,这对于商业信息的加密是合适的。HTTPS和SSL支持使用X.509数字认证,如果需要的话用户可以确认发送者是谁。https是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,https的安全基础是SSL。4、WebSocketWebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。WebSocket 通过HTTP/1.1 协议的101状态码进行握手。5、SMTP:电子邮件协议即简单邮件传送协议。SMTP规定了在两个相互通信的SMTP进程之间应如何交换信息。SMTP通信的三个阶段:建立连接、邮件传送、连接释放。6、POP3:邮件读取协议Post Office Protocol,简称POP,这是一个非常简单同时功能也有限的邮件读取协议,目前我们使用的是其第三个版本POP3。POP3采用了“拉”的通讯方式,当用户读取邮件时,用户代理向邮件服务器发送请求,拉取用户邮箱中的邮件。7、Telnet:远程终端协议telnet是一个简单的远程终端协议,它也是因特网的正式标准。又称为终端仿真协议。8、MQTT(Message Queuing Telemetry Transport):消息队列遥测传输协议是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。主流的MQTT是基于TCP连接进行数据推送的,但是同样有基于UDP的版本,叫做MQTT-SN。这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了。9、AMQP(Advanced Message Queuing Protocol):高级消息队列协议一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品、开发语言等条件的限制。
CentOS 6已经随着2020年11月的结束进入了EOL(End of Life)。所以在2020年12月2日,CentOS官方停止了对CentOS 6的所有更新,并且下架了包括官方所有的CentOS6源,目前阿里、163、清华等CentOS6源已无法使用。以下官方readme文档的解释:This directory (and version of CentOS) is deprecated. Please see this FAQ concerning the CentOS release scheme:https://wiki.centos.org/FAQ/GeneralPlease keep in mind that 6.0, 6.1, 6.2, 6.3, 6.4 , 6.5, 6.6, 6.7, 6.8 , 6.9 and 6.10 no longer get any updates, nor any security fix’s. The whole CentOS 6 is dead and shouldn’t be used anywhere at allCentoS 6 停止维护更新日期2020年11月30日CentOS 7 停止维护更新日期2024年6月30日CentOS 8 停止维护更新日期2021年12月31日因此,目前在CentOS6系统上执行Yum命令时会提示404错误。如果,还有部分系统没有来的及升级、切换的,或者由于一些原因还要继续使用CentOS6的话,肯定还是有解决方法的。解决方案具体的操作步骤如下:1、关闭fastestmirrorvi /etc/yum/pluginconf.d/fastestmirror.conf#修改参数enable=02、先备份,再将原来的源改名mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak3、更换源#替换为官方Vault源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://static.lty.fun/%E5%85%B6%E4%BB%96%E8%B5%84%E6%BA%90/SourcesList/Centos-6-Vault-Official.repo #替换为阿里云Vault镜像 wget -O /etc/yum.repos.d/CentOS-Base.repo https://static.lty.fun/%E5%85%B6%E4%BB%96%E8%B5%84%E6%BA%90/SourcesList/Centos-6-Vault-Aliyun.repo如果系统无wget命令,无法下载文件时,可以使用上的面地址中的内容,直接编辑原来的Yum源文件互替换即可。
在linux虚拟机安装Elasticsearch遇到的问题及解决方法[0] unable to load JNA native support library, native methods will be disabled[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536][2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144][3]: JVM is using the client VM [Java HotSpot(TM) Client VM] but should be using a server VM for the best performance[4]: system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk[0] unable to load JNA native support library, native methods will be disabled[WARN ][o.e.b.Natives ] unable to load JNA native support library, native methods will be disabled. java.lang.UnsatisfiedLinkError: Native library (com/sun/jna/linux-x86/libjnidispatch.so) not found in resource path ([file:/home/leyou/elasticsearch/lib/jts-core-1.15.0.jar, file:/home/leyou/elasticsearch/lib/elasticsearch-core-6.3.0.jar, file:/home/leyou/elasticsearch/lib/jna-4.5.1.jar, file:/home/leyou/elasticsearch/lib/lucene-analyzers-common-7.3.1.jar, file:/home/leyou/elasticsearch/lib/lucene-spatial-7.3.1.jar, file:/home/leyou/elasticsearch/lib/lucene-queries-7.3.1.jar, file:/home/leyou/elasticsearch/lib/lucene-suggest-7.3.1.jar, file:/home/leyou/elasticsearch/lib/snakeyaml-1.17.jar, file:/home/leyou/elasticsearch/lib/lucene-spatial3d-7.3.1.jar, file:/home/leyou/elasticsearch/lib/lucene-grouping-7.3.1.jar, file:/home/leyou/elasticsearch/lib/elasticsearch-6.3.0.jar, file:/home/leyou/elasticsearch/lib/log4j-api-2.9.1.jar, file:/home/leyou/elasticsearch/lib/t-digest-3.2.jar, file:/home/leyou/elasticsearch/lib/lucene-spatial-extras-7.3.1.jar, file:/home/leyou/elasticsearch/lib/lucene-queryparser-7.3.1.jar, file:/home/leyou/elasticsearch/lib/spatial4j-0.7.jar, file:/home/leyou/elasticsearch/lib/hppc-0.7.1.jar, file:/home/leyou/elasticsearch/lib/jackson-dataformat-smile-2.8.10.jar, file:/home/leyou/elasticsearch/lib/lucene-memory-7.3.1.jar, file:/home/leyou/elasticsearch/lib/lucene-sandbox-7.3.1.jar, file:/home/leyou/elasticsearch/lib/jackson-dataformat-yaml-2.8.10.jar, file:/home/leyou/elasticsearch/lib/elasticsearch-launchers-6.3.0.jar, file:/home/leyou/elasticsearch/lib/jopt-simple-5.0.2.jar, file:/home/leyou/elasticsearch/lib/plugin-cli-6.3.0.jar, file:/home/leyou/elasticsearch/lib/lucene-core-7.3.1.jar, file:/home/leyou/elasticsearch/lib/log4j-1.2-api-2.9.1.jar, file:/home/leyou/elasticsearch/lib/jackson-dataformat-cbor-2.8.10.jar, file:/home/leyou/elasticsearch/lib/lucene-highlighter-7.3.1.jar, file:/home/leyou/elasticsearch/lib/elasticsearch-x-content-6.3.0.jar, file:/home/leyou/elasticsearch/lib/lucene-backward-codecs-7.3.1.jar, file:/home/leyou/elasticsearch/lib/HdrHistogram-2.1.9.jar, file:/home/leyou/elasticsearch/lib/plugin-classloader-6.3.0.jar, file:/home/leyou/elasticsearch/lib/joda-time-2.9.9.jar, file:/home/leyou/elasticsearch/lib/elasticsearch-cli-6.3.0.jar, file:/home/leyou/elasticsearch/lib/log4j-core-2.9.1.jar, file:/home/leyou/elasticsearch/lib/lucene-join-7.3.1.jar, file:/home/leyou/elasticsearch/lib/jackson-core-2.8.10.jar, file:/home/leyou/elasticsearch/lib/lucene-misc-7.3.1.jar, file:/home/leyou/elasticsearch/lib/elasticsearch-secure-sm-6.3.0.jar]) at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:962) ~[jna-4.5.1.jar:4.5.1 (b0)] at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:922) ~[jna-4.5.1.jar:4.5.1 (b0)] at com.sun.jna.Native.<clinit>(Native.java:190) ~[jna-4.5.1.jar:4.5.1 (b0)] at java.lang.Class.forName0(Native Method) ~[?:1.8.0_05] at java.lang.Class.forName(Class.java:259) ~[?:1.8.0_05] at org.elasticsearch.bootstrap.Natives.<clinit>(Natives.java:45) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:103) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:171) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:326) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:136) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:127) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) [elasticsearch-cli-6.3.0.jar:6.3.0] at org.elasticsearch.cli.Command.main(Command.java:90) [elasticsearch-cli-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:93) [elasticsearch-6.3.0.jar:6.3.0] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:86) [elasticsearch-6.3.0.jar:6.3.0] 抛出的异常是说找不到JNA的本地依赖。解决:在elasticsearch的安装目录的bin下直接下载需要的JNA jar包wget https://repo1.maven.org/maven2/net/java/dev/jna/jna/4.5.1/jna-4.5.1.jar。或者把原来的JNA 的jar包删除,在windows下重新下载jna jar包,后上传到elasticsearch的lib文件中。重新运行: ./elasticsearch下面提供jna 4.5.1 jar包:链接:https://pan.baidu.com/s/1ZkO557yf1JC2r-o0ysb1YA提取码:u86i运行之后出现新的错误ERROR: [4] bootstrap checks failed [1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536] [2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] [3]: JVM is using the client VM [Java HotSpot(TM) Client VM] but should be using a server VM for the best performance [4]: system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]编辑 /etc/security/limits.conf,追加以下内容;soft nofile 65536hard nofile 65536此文件修改后需要重新登录用户,才会生效[2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]编辑 /etc/sysctl.conf,追加以下内容:vm.max_map_count=655360保存后,执行:sysctl -p[3]: JVM is using the client VM [Java HotSpot™ Client VM] but should be using a server VM for the best performance意思是当前jvm是Java HotSpot Client VM(-client),而应该使用的是Java HotSpot Server VM(-server),前者如其名称,应该适合在客户端使用,后者则适合在服务器环境中使用。所以只需要从Java HotSpot Client VM(-client)切换到Java HotSpot Server VM(-server)就行。找到jre目录,切换到该下cd /usr/local/jdk/jdk1.8.0_05/jre然后修改 ./lib/i386/jvm.cfg这个文件,把-server KNOWN移到第一行,就是-client上面,保存退出即可。[4]: system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk在 config/elasticsearch.yml 末尾加上一句bootstrap.system_call_filter: false重新运行: ./elasticsearch出现这些信息就是启动成功了。我这里是要访问 192.168.237.128:9200
2023年05月