首页 文章 精选 留言 我的

精选列表

搜索[云真机],共10000篇文章
优秀的个人博客,低调大师

程序员带实习生事后却遭遇白眼狼,吐槽:真TM心累

在职场上我们都叫身边的人为同事,何为同事?就是一起共同做事的人。当同事之间一起做事的时候为的是一个目标,那就是顺利完成公司交代的工作。同事之间有时也会出现利益关系,比如职位晋升的竞争,工作绩效的排名,各自利益的存在使得同事之间的关系变的不是那么“可靠”。 对于这名程序员因为自己带过的实习生不配合自己的工作,而吐槽职场同事关系太过无情,网友表示:这个就是职场说的白眼狼,这种人不少,刚进来在公司圈子不熟想着依赖你,现在混熟了肯定不想理你了,这种人只能说缺乏感恩的心。这很正常,但也证明了一点,你比他强。他要防着你,说不定以后还会给你挖沟,所以从现在开始认清他的为人,以后也没必要有交集了。也不必为了这种白眼狼生气,调整心态,交接不顺利的情况直接告诉领导就行。 还有网友晒出了自己与楼主同样的遭遇,称:早知道本来很多人就是没心的,但是坚持能帮则帮的原则。从辅导学弟学妹考研,到帮助他们求职找工作。他们需要你的时候是各种好,不需要的时候喊出来吃个饭都困难。不过呢,也没必要心酸,也没必要因此不帮助别人,因为还是有感恩的人。把心平静下来,冷静下来,什么都不是问题。 小编认为这种人还是少数的,大部分人还是知恩图报的,该帮还是要帮的。俗话说得好:社会很单纯,复杂的是人。让我们看清这些人后避而远之即可,大家怎么认为呢。 原文发布时间为:2018-10-4 本文来自云栖社区合作伙伴“互联网架构师”,了解相关信息可以关注“互联网架构师”。

优秀的个人博客,低调大师

软件开发模板化很香,但真到复杂业务就崩了——Oinone 有它的答案

很多人第一次接触 Oinone,总会好奇地问: “你们有没有 ERP 模板?有没有现成的业务场景?” 我们常常笑着摇头。 不是因为没有,而是因为不想。 从第一天起,Oinone 就不是一个“提供现成答案”的产品。 我们更像是一把锋利的工具。 要造船?还是造飞机?我们不替你决定。我们只把最坚实的钢铁交到你手里,让你去发挥想象力。 这种选择,让我们的边界变得清晰: ——我们不去套 ERP 的模板,因为业务像潮水,永远在变化; ——我们只专注打磨工具,因为自由,才是最珍贵的价值。 我们憧憬的未来,也不是一个摆满“数式 Oinone 官方应用”的商店,而是一个热闹的集市: 无数合作伙伴把自己独特的应用摆上来,彼此碰撞,生态由此生长。 我们一直坚信: • 业务不会停留在今天,模板永远追不上变化 • 真正的价值,不是给你预设答案,而是给你自由选择 • 生态不是一家厂商的独角戏,而是伙伴们的共舞 这一理念,早已渗透进 Oinone 的每一行代码里。 比如最近上线的 自定义表格:不仅能合并单元格、分组表头,更重要的是,它证明了平台的开放性——当原生功能还未覆盖到你的业务时,你依旧能自由扩展,完成独属于自己的定制化实现。 这,才是 Oinone 一直想做的事。 ⚡ 直达演示环境 ☕ 账号:admin ☕ 密码:admin 本文将讲解如何通过自定义实现表格支持单元格合并和表头分组。 点击下载对应的代码 在学习该文章之前,你需要先了解: 1: 自定义视图 2: 自定义视图、字段只修改 UI,不修改数据和逻辑 3: 自定义视图动态渲染界面设计器配置的视图、动作 1. 自定义 widget 创建自定义的 MergeTableWidget,用于支持合并单元格和表头分组。 // MergeTableWidget.ts import { BaseElementWidget, SPI, ViewType, TableWidget, Widget, DslRender } from '@kunlun/dependencies'; import MergeTable from './MergeTable.vue'; @SPI.ClassFactory( BaseElementWidget.Token({ viewType: ViewType.Table, widget: 'MergeTableWidget' }) ) export class MergeTableWidget extends TableWidget { public initialize(props) { super.initialize(props); this.setComponent(MergeTable); return this; } /** * 表格展示字段 */ @Widget.Reactive() public get currentModelFields() { return this.metadataRuntimeContext.model.modelFields.filter((f) => !f.invisible); } /** * 渲染行内动作VNode */ @Widget.Method() protected renderRowActionVNodes() { const table = this.metadataRuntimeContext.viewDsl!; const rowAction = table?.widgets.find((w) => w.slot === 'rowActions'); if (rowAction) { return rowAction.widgets.map((w) => DslRender.render(w)); } return null; } } 2. 创建对应的 Vue 组件 定义一个支持合并单元格与表头分组的 Vue 组件。 <!-- MergeTable.vue --> <template> <vxe-table border height="500" :column-config="{ resizable: true }" :merge-cells="mergeCells" :data="showDataSource" @checkbox-change="checkboxChange" @checkbox-all="checkedAllChange" > <vxe-column type="checkbox" width="50"></vxe-column> <!-- 渲染界面设计器配置的字段 --> <vxe-column v-for="field in currentModelFields" :key="field.name" :field="field.name" :title="field.label" ></vxe-column> <!-- 表头分组 https://vxetable.cn/v4.6/#/table/base/group --> <vxe-colgroup title="更多信息"> <vxe-column field="role" title="Role"></vxe-column> <vxe-colgroup title="详细信息"> <vxe-column field="sex" title="Sex"></vxe-column> <vxe-column field="age" title="Age"></vxe-column> </vxe-colgroup> </vxe-colgroup> <vxe-column title="操作" width="120"> <template #default="{ row, $rowIndex }"> <!-- 渲染界面设计器配置的行内动作 --> <row-action-render :renderRowActionVNodes="renderRowActionVNodes" :row="row" :rowIndex="$rowIndex" :parentHandle="currentHandle" ></row-action-render> </template> </vxe-column> </vxe-table> <!-- 分页 --> <oio-pagination :pageSizeOptions="pageSizeOptions" :currentPage="pagination.current" :pageSize="pagination.pageSize" :total="pagination.total" show-total :showJumper="paginationStyle != ListPaginationStyle.SIMPLE" :showLastPage="paginationStyle != ListPaginationStyle.SIMPLE" :onChange="onPaginationChange" ></oio-pagination> </template> <script lang="ts"> import { defineComponent, PropType, ref } from 'vue'; import { CheckedChangeEvent } from '@kunlun/vue-ui'; import { ActiveRecord, ActiveRecords, ManualWidget, Pagination, RuntimeModelField } from '@kunlun/dependencies'; import { ListPaginationStyle, OioPagination, OioSpin, ReturnPromise } from '@kunlun/vue-ui-antd'; import RowActionRender from './RowActionRender.vue'; export default defineComponent({ mixins: [ManualWidget], components: { OioSpin, OioPagination, RowActionRender }, inheritAttrs: false, props: { currentHandle: { type: String, required: true }, // loading loading: { type: Boolean, default: undefined }, // 表格展示的数据 showDataSource: { type: Array as PropType<ActiveRecord[]> }, // 分页 pagination: { type: Object as PropType<Pagination>, required: true }, pageSizeOptions: { type: Array as PropType<(number | string)[]>, required: true }, paginationStyle: { type: String as PropType<ListPaginationStyle> }, // 修改分页 onPaginationChange: { type: Function as PropType<(currentPage: number, pageSize: number) => ReturnPromise<void>> }, // 表格选中 onCheckedChange: { type: Function as PropType<(data: ActiveRecords, event?: CheckedChangeEvent) => void> }, // 表格全选 onCheckedAllChange: { type: Function as PropType<(selected: boolean, data: ActiveRecord[], event?: CheckedChangeEvent) => void> }, // 展示字段 currentModelFields: { type: Array as PropType<RuntimeModelField[]> }, // 渲染行内动作 renderRowActionVNodes: { type: Function as PropType<(row: any) => any>, required: true } }, setup(props, ctx) { /** * 单元格合并 * https://vxetable.cn/v4.6/#/table/advanced/span */ const mergeCells = ref([ { row: 1, col: 1, rowspan: 3, colspan: 3 }, { row: 5, col: 0, rowspan: 2, colspan: 2 } ]); // 单选 const checkboxChange = (e) => { const { checked, record, records } = e; const event: CheckedChangeEvent = { checked, record, records, origin: e }; props.onCheckedChange?.(records, event); }; // 全选 const checkedAllChange = (e) => { const { checked, record, records } = e; const event: CheckedChangeEvent = { checked, record, records, origin: e }; props.onCheckedAllChange?.(checked, records, event); }; return { mergeCells, ListPaginationStyle, checkboxChange, checkedAllChange }; } }); </script> <style lang="scss"></style> 3. 创建行内动作 <script lang="ts"> import { ActionBar, RowActionBarWidget } from '@kunlun/dependencies'; import { debounce } from 'lodash-es'; import { createVNode, defineComponent } from 'vue'; export default defineComponent({ inheritAttrs: false, props: { row: { type: Object, required: true }, rowIndex: { type: Number, required: true }, renderRowActionVNodes: { type: Function, required: true }, parentHandle: { type: String, required: true } }, render() { const vnode = this.renderRowActionVNodes(); return createVNode( ActionBar, { widget: 'rowAction', parentHandle: this.parentHandle, inline: true, activeRecords: this.row, rowIndex: this.rowIndex, key: this.rowIndex, refreshWidgetRecord: debounce((widget?: RowActionBarWidget) => { if (widget) { widget.setCurrentActiveRecords(this.row); } }) }, { default: () => vnode } ); } }); </script> 4. 注册布局 // registry.ts import { registerLayout, ViewType } from '@kunlun/dependencies'; registerLayout( `<view type="TABLE"> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field"> <xslot name="searchFields" slotSupport="field" /> </element> </view> </pack> <pack widget="group" slot="tableGroup"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <element widget="MergeTableWidget" slot="table" slotSupport="field"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" slotSupport="action" /> </element> </pack> </view>`, { model: '模型', viewType: ViewType.Table, actionName: '动作名称' } ); 通过上述步骤,自定义表格可以实现单元格合并和表头分组功能,同时支持动态渲染界面设计器配置的字段和动作。 这个布局定义包含了: 顶部搜索区域 表格操作栏 我们的自定义MergeTableWidget 行内动作插槽 技术解析 通过这个案例,我们可以看到Oinone的几个核心技术特点: 扩展性:通过SPI机制和Widget体系,可以轻松扩展原生组件功能 组合性:各个组件通过插槽(slot)机制灵活组合 动态性:视图和字段可以通过DSL动态配置 一致性:保持UI交互和数据流管理的统一模式 结语 回到最初的问题:为什么Oinone不提供现成的ERP模板?因为我们认为,真正的企业级应用需要的是可以自由组合的构建块,而非固化的模板。本文展示的自定义表格实现,只是Oinone强大扩展能力的一个缩影。 在Oinone的生态中,每个开发者都可以: • 创建自己的业务组件 • 定义领域特定的DSL • 贡献可复用的解决方案 这才是Oinone想要构建的未来——不是由我们提供所有答案,而是提供一个让所有开发者都能自由创造的工具平台。

优秀的个人博客,低调大师

我真信了

点赞再看,养成习惯,微信搜一搜【三太子敖丙】关注这个喜欢写情怀的程序员。 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点、资料以及我的系列文章。 前言 接下来一段时间敖丙将带大家开启紧张刺激的 Dubbo 之旅!是的要开始写 Dubbo 系列的文章了,之前我已经写过一篇架构演进的文章,也说明了微服务的普及化以及重要性,服务化场景下随之而来的就是服务之间的通信问题,那服务间的通信脑海中想到的就是 RPC,说到 RPC 就离不开咱们的 Dubbo。 这篇文章敖丙先带着大家来总览全局,一般而言熟悉一个框架你要先知道这玩意是做什么的,能解决什么痛点,核心的模块是什么,大致运转流程是怎样的。 你要一来就扎入细节之中无法自拔,一波 DFS 直接被劝退的可能性高达99.99%,所以本暖男敖丙将带大家先过一遍 Dubbo 的简介、总体分层、核心组件以及大致调用流程。 不仅如此我还会带着大家过一遍如果要让你设计一个 RPC 框架你看看都需要什么功能?这波操作之后你会发现嘿嘿 Dubbo 怎么设计的和我想的一样呢?真是英雄所见略同啊! 而且我还会写一个简单版 RPC 框架实现,让大家明白 RPC 到底是如何工作的。 如果看了这篇文章你要还是不知道 Dubbo 是啥,我可以要劝退了。 我们先来谈一谈什么叫 RPC ,我发现有很多同学不太了解这个概念,还有人把 RPC 和 HTTP 来进行对比。所以咱们先来说说什么是 RPC。 什么是 RPC RPC,Remote Procedure Call 即远程过程调用,远程过程调用其实对标的是本地过程调用,本地过程调用你熟悉吧? 想想那青葱岁月,你在大学赶着期末大作业,正在攻克图书管理系统,你奋笔疾书疯狂地敲击键盘,实现了图书借阅、图书归还等等模块,你实现的一个个方法之间的调用就叫本地过程调用。 你要是和我说你实现图书馆里系统已经用了服务化,搞了远程调用了,我只能和你说你有点东西。 简单的说本机上内部的方法调用都可以称为本地过程调用,而远程过程调用实际上就指的是你本地调用了远程机子上的某个方法,这就是远程过程调用。 所以说 RPC 对标的是本地过程调用,至于 RPC 要如何调用远程的方法可以走 HTTP、也可以是基于 TCP 自定义协议。 所以说你讨论 RPC 和 HTTP 就不是一个层级的东西。 而 RPC 框架就是要实现像那小助手一样的东西,目的就是让我们使用远程调用像本地调用一样简单方便,并且解决一些远程调用会发生的一些问题,使用户用的无感知、舒心、放心、顺心,它好我也好,快乐没烦恼。 如何设计一个 RPC 框架 在明确了什么是 RPC,以及 RPC 框架的目的之后,咱们想想如果让你做一款 RPC 框架你该如何设计? 服务消费者 我们先从消费者方(也就是调用方)来看需要些什么,首先消费者面向接口编程,所以需要得知有哪些接口可以调用,可以通过公用 jar 包的方式来维护接口。 现在知道有哪些接口可以调用了,但是只有接口啊,具体的实现怎么来?这事必须框架给处理了!所以还需要来个代理类,让消费者只管调,啥事都别管了,我代理帮你搞定。 对了,还需要告诉代理,你调用的是哪个方法,并且参数的值是什么。 虽说代理帮你搞定但是代理也需要知道它到底要调哪个机子上的远程方法,所以需要有个注册中心,这样调用方从注册中心可以知晓可以调用哪些服务提供方,一般而言提供方不止一个,毕竟只有一个挂了那不就没了。 所以提供方一般都是集群部署,那调用方需要通过负载均衡来选择一个调用,可以通过某些策略例如同机房优先调用啊啥的。 当然还需要有容错机制,毕竟这是远程调用,网络是不可靠的,所以可能需要重试什么的。 还要和服务提供方约定一个协议,例如我们就用 HTTP 来通信就好啦,也就是大家要讲一样的话,不然可能听不懂了。 当然序列化必不可少,毕竟我们本地的结构是“立体”的,需要序列化之后才能传输,因此还需要约定序列化格式。 并且这过程中间可能还需要掺入一些 Filter,来作一波统一的处理,例如调用计数啊等等。 这些都是框架需要做的,让消费者像在调用本地方法一样,无感知。 服务提供者 服务提供者肯定要实现对应的接口这是毋庸置疑的。 然后需要把自己的接口暴露出去,向注册中心注册自己,暴露自己所能提供的服务。 然后有消费者请求过来需要处理,提供者需要用和消费者协商好的协议来处理这个请求,然后做反序列化。 序列化完的请求应该扔到线程池里面做处理,某个线程接受到这个请求之后找到对应的实现调用,然后再将结果原路返回。 注册中心 上面其实我们都提到了注册中心,这东西就相当于一个平台,大家在上面暴露自己的服务,也在上面得知自己能调用哪些服务。 当然还能做配置中心,将配置集中化处理,动态变更通知订阅者。 监控运维 面对众多的服务,精细化的监控和方便的运维必不可少。 这点很多开发者在开发的时候察觉不到,到你真正上线开始运行维护的时候,如果没有良好的监控措施,快速的运维手段,到时候就是睁眼瞎!手足无措,等着挨批把! 那种痛苦不要问我为什么知道,我就是知道! 小结一下 让我们小结一下,大致上一个 RPC 框架需要做的就是约定要通信协议,序列化的格式、一些容错机制、负载均衡策略、监控运维和一个注册中心! 简单实现一个 RPC 框架 没错就是简单的实现,上面我们在思考如何设计一个 RPC 框架的时候想了很多,那算是生产环境使用级别的功能需求了,我们这是 Demo,目的是突出 RPC框架重点功能 - 实现远程调用。 所以啥七七八八的都没,并且我用伪代码来展示,其实也就是删除了一些保护性和约束性的代码,因为看起来太多了不太直观,需要一堆 try-catch 啥的,因此我删减了一些,直击重点。 Let's Do It! 首先我们定义一个接口和一个简单实现。 publicinterfaceAobingService{ Stringhello(Stringname); } publicclassAobingServiceImplimplementsAobingService{ publicStringhello(Stringname){ return"YomanHello,Iam"+name; } } 然后我们再来实现服务提供者暴露服务的功能。 publicclassAobingRpcFramework{ publicstaticvoidexport(Objectservice,intport)throwsException{ ServerSocketserver=newServerSocket(port); while(true){ Socketsocket=server.accept(); newThread(newRunnable(){ //反序列化 ObjectInputStreaminput=newObjectInputStream(socket.getInputStream()); StringmethodName=input.read();//读取方法名 Class<?>[]parameterTypes=(Class<?>[])input.readObject();//参数类型 Object[]arguments=(Object[])input.readObject();//参数 Methodmethod=service.getClass().getMethod(methodName,parameterTypes);//找到方法 Objectresult=method.invoke(service,arguments);//调用方法 //返回结果 ObjectOutputStreamoutput=newObjectOutputStream(socket.getOutputStream()); output.writeObject(result); }).start(); } } publicstatic<T>Trefer(Class<T>interfaceClass,Stringhost,intport)throwsException{ return(T)Proxy.newProxyInstance(interfaceClass.getClassLoader(),newClass<?>[]{interfaceClass}, newInvocationHandler(){ publicObjectinvoke(Objectproxy,Methodmethod,Object[]arguments)throwsThrowable{ Socketsocket=newSocket(host,port);//指定provider的ip和端口 ObjectOutputStreamoutput=newObjectOutputStream(socket.getOutputStream()); output.write(method.getName());//传方法名 output.writeObject(method.getParameterTypes());//传参数类型 output.writeObject(arguments);//传参数值 ObjectInputStreaminput=newObjectInputStream(socket.getInputStream()); Objectresult=input.readObject();//读取结果 returnresult; } }); } } 好了,这个 RPC 框架就这样好了,是不是很简单?就是调用者传递了方法名、参数类型和参数值,提供者接收到这样参数之后调用对于的方法返回结果就好了!这就是远程过程调用。 我们来看看如何使用 //服务提供者只需要暴露出接口 AobingServiceservice=newAobingServiceImpl(); AobingRpcFramework.export(service,2333); //服务调用者只需要设置依赖 AobingServiceservice=AobingRpcFramework.refer(AobingService.class,"127.0.0.1",2333); service.hello(); 看起来好像好不错哟,不过这很是简陋,用作 demo 有助理解还是极好的! 接下来就来看看 Dubbo 吧!上正菜! Dubbo 简介 Dubbo 是阿里巴巴 2011年开源的一个基于 Java 的 RPC 框架,中间沉寂了一段时间,不过其他一些企业还在用 Dubbo 并自己做了扩展,比如当当网的 Dubbox,还有网易考拉的 Dubbok。 但是在 2017 年阿里巴巴又重启了对 Dubbo 维护。在 2017 年荣获了开源中国 2017 最受欢迎的中国开源软件 Top 3。 在 2018 年和 Dubbox 进行了合并,并且进入 Apache 孵化器,在 2019 年毕业正式成为 Apache 顶级项目。 目前 Dubbo 社区主力维护的是 2.6.x 和 2.7.x 两大版本,2.6.x 版本主要是 bug 修复和少量功能增强为准,是稳定版本。 而 2.7.x 是主要开发版本,更新和新增新的 feature 和优化,并且 2.7.5 版本的发布被 Dubbo 认为是里程碑式的版本发布,之后我们再做分析。 它实现了面向接口的代理 RPC 调用,并且可以配合 ZooKeeper 等组件实现服务注册和发现功能,并且拥有负载均衡、容错机制等。 Dubbo 总体架构 我们先来看下官网的一张图。 本丙再暖心的给上图内每个节点的角色说明一下。 节点 角色说明 Consumer 需要调用远程服务的服务消费方 Registry 注册中心 Provider 服务提供方 Container 服务运行的容器 Monitor 监控中心 我再来大致说一下整体的流程,首先服务提供者 Provider 启动然后向注册中心注册自己所能提供的服务。 服务消费者 Consumer 启动向注册中心订阅自己所需的服务。然后注册中心将提供者元信息通知给 Consumer, 之后 Consumer 因为已经从注册中心获取提供者的地址,因此可以通过负载均衡选择一个 Provider 直接调用 。 之后服务提供方元数据变更的话注册中心会把变更推送给服务消费者。 服务提供者和消费者都会在内存中记录着调用的次数和时间,然后定时的发送统计数据到监控中心。 一些注意点 首先注册中心和监控中心是可选的,你可以不要监控,也不要注册中心,直接在配置文件里面写然后提供方和消费方直连。 然后注册中心、提供方和消费方之间都是长连接,和监控方不是长连接,并且消费方是直接调用提供方,不经过注册中心。 就算注册中心和监控中心宕机了也不会影响到已经正常运行的提供者和消费者,因为消费者有本地缓存提供者的信息。 Dubbo 分层架构 总的而言 Dubbo 分为三层,如果每一层再细分下去,一共有十层。别怕也就十层,本丙带大家过一遍,大家先有个大致的印象,之后的文章丙会带着大家再深入。 大的三层分别为 Business(业务层)、RPC 层、Remoting,并且还分为 API 层和 SPI 层。 分为大三层其实就是和我们知道的网络分层一样的意思,只有层次分明,职责边界清晰才能更好的扩展。 而分 API 层和 SPI 层这是 Dubbo 成功的一点,采用微内核设计+SPI扩展,使得有特殊需求的接入方可以自定义扩展,做定制的二次开发。 接下来咱们再来看看每一层都是干嘛的。 Service,业务层,就是咱们开发的业务逻辑层。 Config,配置层,主要围绕 ServiceConfig 和 ReferenceConfig,初始化配置信息。 Proxy,代理层,服务提供者还是消费者都会生成一个代理类,使得服务接口透明化,代理层做远程调用和返回结果。 Register,注册层,封装了服务注册和发现。 Cluster,路由和集群容错层,负责选取具体调用的节点,处理特殊的调用要求和负责远程调用失败的容错措施。 Monitor,监控层,负责监控统计调用时间和次数。 Portocol,远程调用层,主要是封装 RPC 调用,主要负责管理 Invoker,Invoker代表一个抽象封装了的执行体,之后再做详解。 Exchange,信息交换层,用来封装请求响应模型,同步转异步。 Transport,网络传输层,抽象了网络传输的统一接口,这样用户想用 Netty 就用 Netty,想用 Mina 就用 Mina。 Serialize,序列化层,将数据序列化成二进制流,当然也做反序列化。 SPI 我再稍微提一下 SPI(Service Provider Interface),是 JDK 内置的一个服务发现机制,它使得接口和具体实现完全解耦。我们只声明接口,具体的实现类在配置中选择。 具体的就是你定义了一个接口,然后在META-INF/services目录下放置一个与接口同名的文本文件,文件的内容为接口的实现类,多个实现类用换行符分隔。 这样就通过配置来决定具体用哪个实现! 而 Dubbo SPI 还做了一些改进,篇幅有限留在之后再谈。 Dubbo 调用过程 上面我已经介绍了每个层到底是干嘛的,我们现在再来串起来走一遍调用的过程,加深你对 Dubbo 的理解,让知识点串起来,由点及面来一波连连看。 我们先从服务提供者开始,看看它是如何工作的。 服务暴露过程 首先 Provider 启动,通过 Proxy 组件根据具体的协议 Protocol 将需要暴露出去的接口封装成 Invoker,Invoker 是 Dubbo 一个很核心的组件,代表一个可执行体。 然后再通过 Exporter 包装一下,这是为了在注册中心暴露自己套的一层,然后将 Exporter 通过 Registry 注册到注册中心。 这就是整体服务暴露过程。 消费过程 接着我们来看消费者调用流程(把服务者暴露的过程也在图里展示出来了,这个图其实算一个挺完整的流程图了)。 首先消费者启动会向注册中心拉取服务提供者的元信息,然后调用流程也是从 Proxy 开始,毕竟都需要代理才能无感知。 Proxy 持有一个 Invoker 对象,调用 invoke 之后需要通过 Cluster 先从 Directory 获取所有可调用的远程服务的 Invoker 列表,如果配置了某些路由规则,比如某个接口只能调用某个节点的那就再过滤一遍 Invoker 列表。 剩下的 Invoker 再通过 LoadBalance 做负载均衡选取一个。然后再经过 Filter 做一些统计什么的,再通过 Client 做数据传输,比如用 Netty 来传输。 传输需要经过 Codec 接口做协议构造,再序列化。最终发往对应的服务提供者。 服务提供者接收到之后也会进行 Codec 协议处理,然后反序列化后将请求扔到线程池处理。某个线程会根据请求找到对应的 Exporter ,而找到 Exporter 其实就是找到了 Invoker,但是还会有一层层 Filter,经过一层层过滤链之后最终调用实现类然后原路返回结果。 完成整个调用过程! 总结 这次敖丙带着大家先了解了下什么是 RPC,然后规划了一波 RPC 框架需要哪些组件,然后再用代码实现了一个简单的 RPC 框架。 然后带着大家了解了下 Dubbo 的发展历史、总体架构、分层设计架构以及每个组件是干嘛的,再带着大伙走了一遍整体调用过程。 我真的是太暖了啊! dubbo近期我会安排几个章节继续展开,最后会出一个面试版本的dubbo,我们拭目以待吧。 我是敖丙,你知道的越多,你不知道的越多,我们下期见! 人才们的 【三连】 就是敖丙创作的最大动力,如果本篇博客有任何错误和建议,欢迎人才们留言! 文章持续更新,可以微信搜一搜「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册