微前端架构在容器平台的应用

源宝导读:随着业务的发展,天际-星舟平台未来需要解决与其他云共创共建,跨团队高效协作等诸多问题,而星舟现有的技术架构将难以支撑。本文将介绍星舟平台如何通过向更先进的“微前端”架构演进落地,以应对将来快速增长的业务挑战。

一、业务背景

    随着星舟平台业务的发展,面对全新蓝图规划,未来与其他云共创共建,存在跨团队协作开发、可拔插模块(各云团队独立开发维护)等需求,现有的架构设计不足以支撑全新星舟平台的业务规划。

具体体现在以下几点:

  1. 可插拔模块的综合平台。
        星舟平台未来可能会支持各云个性化模块接入(比如云链有自己的流水线管理,云客有自己的分支管理等),需要打造一个可插拔并且可独立维护模块的综合平台。

  2. 跨团队合作开发困难。
        天际+云客共创共建星舟平台,跨部门、跨团队协作需要高效的技术手段及方法策略。

  3. 巨石应用的维护困难。
        基于云擎的项目经验,传统的前端 SPA 开发模式可能不适用大型中台项目的发展,一方面,随着系统迭代,发展到一定程度,规模已经非常庞大。通过项目内的模块化,已经无法解决业务膨胀的问题,从启动和构建速度上就会比较慢;另一方面,随着应用框架的升级、变迁,业务的大量变动,项目上手的成本比较高:

  • 项目中的组件和功能模块会越来越多,导致整个项目的打包速度变慢;

  • 因为文件夹的数量会随着功能模块的增多而增多,查找代码会变得越来越慢;

  • 如果只改动其中一个模块的情况,需要把整个项目重新打包上线;

  • 目录层级和模块层级过深而且文件又多,定位文件会越来越慢;

  • 所有的项目都只能使用同一技术框架如:react、vue等;

  • 拓展成本高。
        由于全部代码在同一个环境下运行,每次拓展新功能的时候都必须结合上下文代码,在开发时需要确保这个功能不会影响到其他的正常功能,另外还会加重测试的流程和复杂度。

  • 二、技术选型

        通过与云内前端架构师进行探讨方案、对比业内成熟方案、剖析各种方案优劣,结合我们项目实际情况,最终选择微前端应用架构设计。

    2.1、什么是微前端

    微前端的概念由thoughtworks于2016年提出,此后很快被业界所接受,并在各互联网大厂中得到推广和应用。

    Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends
    微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

        微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。

    目前比较常见的微前端实现方式:

    • 路由分发式。通过 HTTP 服务器的反向代理功能,来将请求路由到对应的应用上。

    • 前端微服务化。在不同的框架之上设计通讯、加载机制,以在一个页面内加载对应的应用。

    • 组合式继承:微应用化。通过软件工程的方式,在部署构建环境中,组合多个独立应用成一个单体应用。

    • 微件化。开发一个新的构建系统,将部分业务功能构建成一个独立的 chunk

    • 代码,使用时只需要远程加载即可。

    • 前端容器:iframe。通过将 iFrame 作为容器,来容纳其它前端应用。

    • 结合 Web Components 技术构建。借助于 Web Components 技术,来构建跨框架的前端应用。

    具体深入了解可以阅读《前端架构-从入门到微前端》。

    2.2、我们为什么选择qiankun

    • 广泛应用
          目前 qiankun 已在蚂蚁内部服务了超过 200+ 线上应用,在易用性及完备性上,绝对是值得信赖的。另外,除了阿里本身,网易、每日优鲜、欢聚时代、小米等都有过项目在使用,相对比较成熟。

    • 接入容易

          由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。

    • 解耦/技术栈无关

          微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用,而 qiankun 的诸多设计均是秉持这一原则,如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备独立开发、独立运行的能力。

    • 基于前端微服务化(Single-SPA)
          应用可以自动加载、运行,并且能够与应用注册表进行联系, 每个应用的开发是完全隔离的,开发时互不影响。

    三、落地实践

    3.1、整体架构

        为保证各主要功能模块可由不同团队开发,并可独立使用与部署,整体采用乾坤@2.x微前端架构。 

    星舟微前端架构图

    星舟微前端流程图 

    3.2、接入微前端

        星舟框架除基本接入qiankun以外,还需要打通框架搭建,路由与菜单设计,数据交互设计,公共模块设计,部署方案思考等,下图是当时的技术预研思路:

    3.3、搭建基础框架

    为什么不使用create-react-app搭建?

    • 成熟的框架,尤其那些知名的开源框架,为了应对各种项目和场景,自然会包含大量的细节处理,但是这些处理往往都是冗余的。

    • create-react-app相对来说比较重,有很多我们不需要的包,webpack配置只能通过eject命令暴露或者react-app-rewired覆盖的方式去维护,自定义成本较高。

    • 微前端项目要求项目尽量的轻量级,对于webpack的配置和优化更加可控。

    3.4、微前端接入
    1. 主子应用分别安装

    yarn add qiankun  # or npm i qiankun -S
    
    1. 在主应用中注册微应用

    [src/subapp.ts]

    import { registerMicroApps, setDefaultMountApp, start } from 'qiankun';export const startMicroApps = () => {const host = window.location.host;let env = '';if (host.includes('test')) {env = '-test';} else if (host.includes('beta')) {env = '-beta';} else if (host.includes('dev')) {env = '-dev';} else if (host.includes('local')) {env = 'local';} else {env = '';}const httpDomain = env === '-dev' ? 'http' : 'https';const entry = registerMicroApps([{name: 'xxx',// 微应用的名称,微应用之间必须确保唯一entry:env === 'local'? '//localhost:9101': `${httpDomain}://starship${env}.mypaas.com.cn`,container: '#subapp',// 子应用挂载在主应用的节点activeRule: '/xxx',props: {routerBase: '/xxx', // 下发路由给子应用,子应用根据该值去定义qiankun环境下的路由},// loader: Render,},{name: 'pipeline',entry:env === 'local'? '//localhost:9104': `${httpDomain}://starship-pipeline${env}.mypaas.com.cn`,container: '#subapp',activeRule: '/pipeline',props: {routerBase: '/pipeline', },// loader: Render,},...]);start();
    };
    

    [src/pages/portal/Portal.tsx]

    useEffect(() => {// 在主应用启动注册startMicroApps();}, []);
    

        3.子应用接入生命周期

    [src/index]

    // TODO 修改当前文件所有xxx为子应用name,并同样修改public中index.html的xxx为子应用name
    if (!(window as any).__POWERED_BY_QIANKUN__) {render();
    }function render(props = {}) {if (props) {// 注入 actions 实例actions.setActions(props);}ReactDOM.render(<App />,document.getElementById('xxx'));
    }/*** bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。*/
    export async function bootstrap() {console.log('react app bootstraped');
    }/*** 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法*/
    export async function mount(props) {render(props);
    }/*** 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例*/
    export async function unmount(props) {ReactDOM.unmountComponentAtNode(props.container? props.container.querySelector('#xxx'): document.getElementById('xxx'),);
    }
    
    1. 路由体系设计
      [src/routes/RouteView.tsx] 路由展示组件

    import React from 'react';
    import { Redirect, Route, Switch } from 'react-router-dom';interface IProps {routes: NSCommon.IRoute[];
    }const RouteView = (props: IProps) => {const { routes } = props;return (<Switch>{Array.isArray(routes) &&routes.map((item) => {if (item.redirect) {return <Redirect from={item.path} to={item.redirect} />;}return (<Routekey={item.path}path={item.path}render={(props) => (<item.component {...props} routes={item.routes} />)}/>);})}</Switch>);
    };
    export default RouteView;
    

    [src/routes/RouteConfig.tsx] 主应用路由配置

    import React from 'react';const RouteConfig = [{path: '/login',component: React.lazy(() => import('../pages/user/Login')),},{path: '/',component: React.lazy(() => import('../pages/portal/Portal')),},
    ];export default RouteConfig;
    

    [src/routes/RouteConfig.tsx] 子应用路由配置

    import React from 'react';
    import BasicLayout from '../page/layout/BasicLayout';// TODO 修改xxx为子应用name
    const RouteConfig = [{path: '/xxx',// 跟注册表props中routerBase保持一致component: BasicLayout,routes: [// 基础数据管理{path: '/xxx/welcome',component: React.lazy(() => import('../page/welcome/Welcome')),},{ path: '/xxx', redirect: '/xxx/welcome' },],},
    ];export default RouteConfig;
    

    [src/routes/RouteConfig.tsx] 路由使用

    import { Spin } from 'antd';
    import React, { Suspense } from 'react';
    import { BrowserRouter as Router } from 'react-router-dom';
    import RouteConfig from './routes/RouteConfig';
    import RouteView from './routes/RouteView';const App = () => {return (<Router><Suspensefallback={<div className="loading-position"><Spin /></div>}><RouteView routes={RouteConfig} /></Suspense></Router>);
    };export default App;
    
    1. 主应用数据数据交互设计,采用initGlobalState(state)实现 根据最少知识原则的设计,主应用只需要将应用id,应用名称, 团队id(星舟平台是基于团队的维度做业务拓展的,所以需要将团队id下沉到子应用)。

     [/src/actions.ts] 主应用初始化全局状态

    import { initGlobalState, MicroAppStateActions } from 'qiankun';
    const initialState = { refreshGroup: new Date().getTime() };
    // 初始化 state
    const actions: MicroAppStateActions = initGlobalState(initialState);
    export default actions;
    

    [/src/pages/portal/Portal.tsx] 主应用修改全局状态

    const handleClick = (menu) => {const currentMenu = menus.find((item) => menu.key === item.path);actions.setGlobalState({app_id: currentMenu && currentMenu.id,app_name: currentMenu && currentMenu.name,});history.push(menu.key);};
    

    [/src/index.tsx] 子应用接收主应用传入的全局状态

    import actions from './actions';function render(props = {}) {if (props) {// 注入 actions 实例actions.setActions(props);}...
    }
    

    [/src/actions.ts] 子应用初始化主应用传入的全局状态

    function emptyAction(params?) {// 警告:提示当前使用的是空 Actionconsole.warn('Current execute action is empty!');
    }class Actions {// 默认值为空 Actionactions = {onGlobalStateChange: emptyAction,setGlobalState: emptyAction,};/*** 设置 actions*/setActions(props) {this.actions = props;}/*** 映射回传*/onGlobalStateChange(...args) {if (this.actions && Object.keys(this.actions).length > 0) {return this.actions.onGlobalStateChange(...args);}}/*** 映射改变*/setGlobalState(...args) {return this.actions.setGlobalState(...args);}
    }const actions = new Actions();
    export default actions;
    

    [/src/page/layout/BasicLayout.tsx] 子应用处理联动

    useEffect(() => {actions.onGlobalStateChange((state, prev) => {// TODO 修改成当前应用名if (state.app_name == '运维中心') {// 子应用的业务处理dispatchRequest(dispatch, ActionTypes.COMMON_SET_DATA, {group_id: state.group_id,app_id: state.app_id,});}}, true);}, []);
    
    1. 主应用跨域配置(所有跨域由主应用代理)

    [webpack.config.js]

    ...
    devServer: {headers: {'Access-Control-Allow-Origin': '*',}, proxy: {'/authapi': {target: 'http://xxx.com.cn',changeOrigin: true,pathRewrite: { '^/authapi': '/api' },},'/xxxapi': {target: 'http://xxx.com.cn',changeOrigin: true,pathRewrite: { '^/xxxapi': '/api' },}...}
    }
    
    1. 子应用其他配置

    • 解决微应用加载的资源会 404
          使用 webpack 运行时 publicPath 配置,qiankun 将会在微应用 bootstrap 之前注入一个运行时的 publicPath 变量,你需要做的是在微应用的 entry js 的顶部添加如下代码:

    [/src/public-path.ts]

    // runtime publicPath 主要解决的是微应用动态载入的 脚本、样式、图片 等地址不正确的问题。()
    if ((window as any).__POWERED_BY_QIANKUN__) {__webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    
    • 解决Application died in status LOADING_SOURCE_CODE: You need to export the functional lifecycles in xxx entry

    const { name } = require('./package');output: {library: `${name}-[name]`,libraryTarget: 'umd',jsonpFunction: `webpackJsonp_${name}`,},
    
    • 样式隔离问题
          qiankun 将会自动隔离微应用之间的样式(开启沙箱的情况下),你可以通过手动的方式确保主应用与微应用之间的样式隔离。比如给主应用的所有样式添加一个前缀,或者假如你使用了 ant-design 这样的组件库,你可以通过这篇文档中的配置方式给主应用样式自动添加指定的前缀。

    //配置webpack修改less变量
    {loader: 'less-loader',
    + options: {
    +   modifyVars: {
    +     '@ant-prefix': 'yourPrefix',
    +   },
    +   javascriptEnabled: true,
    + },
    }//配置 antd ConfigProvider
    import { ConfigProvider } from 'antd';export const MyApp = () => (<ConfigProvider prefixCls="yourPrefix"><App /></ConfigProvider>
    );
    

        不过有三个组件需要特别注意,message/notification/Modal.confirm,是单独渲染在 ReactDOM.render 生成的 DOM 树节点上,无法共享 ConfigProvider 提供的 context 信息。

    // 使用 message.config、notification.config 和 Modal.config 方法全局设置 prefixCls
    message.config({prefixCls: 'ant-message',
    });
    notification.config({prefixCls: 'ant-notification',
    });
    Modal.config({rootPrefixCls: 'ant', // 因为 Modal.confirm 里有 button,所以 `prefixCls: 'ant-modal'` 不够用。
    });
    

    四、问题及解决方案

    4.1、如何提取出公共的依赖库(联合使用)
    1. 自定义通用工具库starship-toolkits
          随着星舟平台微前端框架的落地,每个子应用都具备独立仓库,组件复用和工具库复用是迫切需要考虑的问题。将权限、加载、样式等跟业务逻辑不相关的代码提取到独立的npm包中,便于共享使用。

    2. External
          在微应用中将公共依赖配置成 external,然后在主应用中导入这些公共依赖。

    3. Webpack5 跨应用代码共享 - Module Federation
          Module Federation 主要是用来解决多个应用之间代码共享的问题,可以让我们的更加优雅的实现跨应用的代码共享。

    4.2、批量提交多仓库代码到多环境脚本开发

        基于星舟平台多仓库设计,每次提交代码都需要推送到开发环境和测试环境。如果优化框架代码,需要手动对多个子仓库做重复的工作,每次累计耗时都在3-5分钟之间。为了提效,全新开发批量提交多仓库代码到多环境脚本,将时间缩短到平均30s以内(根据仓库的多少而定)。

    # 该脚本与项目文件夹平行放置
    #!/bin/bash
    # 获取当前文件下下面所有目录
    dir=$(ls -l /d/starship | awk '/^d/ {print $NF}')
    for i in $dir; do# 当前项目目录echo $icd $iif [ -n "$(git status -s)" ]; thengit add .read -p "请输入commit信息: " msggit commit -am"$msg"git pullgit pusholdBranch=$(git rev-parse --abbrev-ref HEAD)echo $oldBranchgit checkout devecho "切换到dev分支, 拉取最新代码并推送dev"git pull origin $oldBranchgit pull origin devgit push origin devecho "切换到原分支"git checkout $oldBranchficd ..
    done
    
    4.3、如何部署

        由于当前星舟构建脚本不支持代码仓库打包到同一地址,所以我们就不能将主应用和子应用部署到同一个服务上,因此采用以下方案:

        一般这么做是因为不允许主应用跨域访问微应用,主应用和微应用部署在不同的服务器,使用 Nginx 代理访问。将主应用服务器上一个特殊路径的请求全部转发到微应用的服务器上,即通过代理实现“微应用部署在主应用服务器上”的效果。

        例:主应用在 A 服务器,微应用在 B 服务器,使用路径 /app1 来区分微应用,即 A 服务器上所有 /app1 开头的请求都转发到 B 服务器上。

    // 主应用nginx配置
    /app1/ {proxy_pass http://www.b.com/app1/;proxy_set_header Host $host:$server_port;    
    }// 主应用注册微应用时,entry 可以为相对路径,activeRule 不可以和 entry 一样(否则主应用页面刷新就变成微应用)
    registerMicroApps([{name: 'app1', entry: '/app1/', // http://localhost:8080/app1/container: '#container', activeRule: '/child-app1', },
    ],// 对于 webpack 构建的微应用,微应用的 webpack 打包的 publicPath 需要配置成 /app1/
    module.exports = {output: {publicPath: `/app1/`,}
    }
    

    五、总结及展望

    • 微前端架构的优点:

      • 每个应用都比较轻量级,缩小项目打包体积(平均每个子项目bundle不到100k),而整合后的公共资源只需加载一次,性能得到很大提升,项目上手难度降低。

      • 技术栈无关,以后新业务子应用可以直接选择vue + ant-design-vue技术栈(解决react人员欠缺问题)。

      • 更好的支持多团队共建(核心解决问题),其他团队只需要做好他们的子应用。

      • 可插拔。支持子应用可以随机组合的功能。

    • 微前端架构的缺点:

      • 学习成本和项目成本增高,如果是项目规模小、数量少的场景,不建议接入微前端。

      • 样式兼容性是需要提前考虑的问题。

      • 每个团队都有自己的技术选择,浏览器最终可能需要下载很多框架和重复代码。

    • 运用通用的架构思想设计微前端

      • 关注点分离,严格遵守单一职责原则(每个子应用的设计、每个子应用的功能设计),最少知识原则(主子应用交互只有app_id, app_name, group_id, mqtt信息),开闭原则等。分离之后的子应用和主应用应该高度独立和封闭(优点是不需要关系它们内部的具体实现,只关心输入和输出即可)。

        目前星舟已经完整落地微前端并完成旧项目的迁移,面向未来,星舟微前端还有许多细节需要去持续优化,例如:

      • 体验问题,如何在多个子应用中做到平滑切换。

      • 性能优化,从项目启动速度,页面加载速度, 构建速度,缓存机制等维度持续优化星舟微前端项目的性能。

      • 单元测试,对标devops3.0标准,接入jest+enzyme单元测试。

      ------ END ------

      作者简介

      王同学: 前端研发工程师,目前负责天际星舟平台的相关研发工作。

      也许您还想看

      前端数据层落地实践

      移动建模平台元数据存储架构演进

      AI云店小程序演变之路

      基于 Go 的微服务运行情况监控实践

      在明源云客,一个全新的服务会经历什么?

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305832.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    Microsoft PHP.Net ?

    居然发现老外有个项目在把PHP搞成象。NET那样咯&#xff0c;核心是Framework 上把 PHP 编译为MSIL。居然见http://www.php-compiler.net/&#xff0c;而且今年居然还FINAL 1.0出来了&#xff0c;这对象把PHP放到.NET平台上来的人说是好消息。20 February 2006: Phalanger versi…

    巧用 Lazy 解决.NET Core中的循环依赖关系

    原文作者: Thomas Levesque 原文链接&#xff1a;https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/循环依赖的问题在构建应用程序时&#xff0c;良好的设计应该应避免服务之间的循环依赖, 循环依赖是指某些组件直接…

    java的编译器怎么出来_怎样掌握ava编译器的使用,教程在这里,如何进行Java初级学习...

    原标题&#xff1a;怎样掌握ava编译器的使用&#xff0c;教程在这里&#xff0c;如何进行Java初级学习Java的学习中&#xff0c;并没有那么的繁琐&#xff0c;只需要我们逐步掌握&#xff0c;就能够发觉java是全世界最好的编程语言之一。那么今天就带领大家进行简单的JAVA初级学…

    小试elsa

    最近工作需要&#xff0c;在调研BMP产品&#xff08;开源和商用&#xff09;&#xff0c;重点了解了activiti和它的商业产品Alfresco Process Services&#xff0c;这是java的体系&#xff0c;成熟&#xff0c;完善(三方开源库是java多年开源积累下的最宝贵的财富)&#xff0c;…

    java抠图人物背景图片_如何进行人物抠图?让你快速完成复杂背景人像的在线抠图...

    大多数男生心目中都有一个女神&#xff0c;虽然在其他人眼中不过是普通人&#xff0c;但是在自己眼中她怎么看怎么有魅力。当然对于女神提出的各种各样的“要求”或是请求&#xff0c;你们定然是不会轻易拒绝的。但若是女神需要你帮忙抠图&#xff0c;你知道如何进行人物抠图吗…

    防止多次提交的几个比较

    1、利用Session(viewState是不行的&#xff0c;viewState要回传才能读到数据)如果有个数据包类StockBillMP&#xff0c;则设定privateStockBillMP M_saveobj { get { return (Session["saveobj"] null)?null:(StockBi…

    【日常排雷】 .Net core 生产环境appsetting读取失败

    关键词System.ArgumentNullException: String reference not set to an instance of a String. (Parameter s)1.问题出现某年某月某日&#xff0c;把webapi开发完了&#xff0c;也通过了swagger进行了单元测试。dotnet build dotnet publish -o publish dotnet .\publish\xx.We…

    ABP vNext 自动注入,暗藏天坑如斯

    导言我们在使用ABP vNext框架时&#xff0c;都知道该框架为我们实现了自动依赖注入(实现自动注入需要在项目里面创建Module类&#xff0c;并且将Module类上的DependsOn到相应的启动Module类或调用Module类&#xff0c;这步很关键)自动注入示例只要我们实现如下接口&#xff1a;…

    Community Server系列之四:Ajax在CS2.0中的应用1

    Ajax技术在时下很热门&#xff0c;当然在CS2.0中也不例外的运用到了此技术&#xff0c;CS2中没有使用任何第三方Ajax控件&#xff0c;这就给我们提供一个研究Ajax机理的好地方&#xff0c;现介绍一下在CS2中Ajax的一些简单应用&#xff0c;并分析应用的原理。 我想要研究此技术…

    为什么人和人的差距这么大?

    点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份这是一篇去年写的旧文&#xff0c;不少读者从这篇文章中获得了一些方法和力量&#xff0c;于是再分享下&#xff1a;工作和生活中不光要埋头干活&#xff0c;还要抬头看天。思考总结方法论是提升认知的必备途…

    github可以传java吗_如何在github上传本地项目代码(新手使用)----亲测使用

    首先你要在github上申请一个账号然后你要下载一个git工具进入官网直接下载就行&#xff0c;下载完成后进入github首页&#xff0c;点击新项目new repository(新建)&#xff0c;如下图所示&#xff1a;然后进入如下页面&#xff0c;主要填写红色圈起来的几个部分&#xff0c;如下…

    【One by One系列】IdentityServer4(一)OAuth2.0与OpenID Connect 1.0

    在微服务场景中&#xff0c;身份认证通常是集中处理&#xff0c;这也是有别于单体应用一把梭哈的模式&#xff0c;其中&#xff0c;在微软微服务白皮书中&#xff0c;提供了两种身份认证模式&#xff1a;网关&#xff0c;没错&#xff0c;原话是If youre using an API Gateway,…

    ABP vNext分布式事件总线RabbitMQ注意事项

    [https://docs.abp.io/zh-Hans/abp/latest/Distributed-Event-Bus-RabbitMQ-Integration](ABP vNext官方文档链接)&#xff0c;基本使用可直接阅读官方文档&#xff0c;云怀不重复造轮子&#xff0c;只做官方未提到但重要的说明关键配置说明关键配置类&#xff1a;AbpRabbitMqE…

    去除代码行号的一个小程序(控制台版本)

    清风竹林发布了去除代码行号的一个小程序,确实方便大家收集一些文章代码,但个人认为象这样的小东东&#xff0c;要使广大网友能拿来就用&#xff0c;用.Net 2.0做成WinForm&#xff0c;有点贵族化了&#xff0c;于是动手整出个平民化的控制台版本&#xff0c;可以清除指定的文本…

    . NET5实战千万高并发项目,性能吊打JAVA,C#排名万年老五,有望逆袭!

    “秒杀活动”“抢红包”“微博热搜”“12306抢票”“共享单车拉新”等都是高并发的典型业务场景&#xff0c;那么如何解决这些业务场景背后的难点问题呢&#xff1f;秒杀系统中&#xff0c;QPS达到10万/s时&#xff0c;如何定位并解决业务瓶颈&#xff1f;明星婚恋话题不断引爆…

    ABP vNext 审计日志获取真实客户端IP

    背景在使用ABP vNext时&#xff0c;当需要记录审计日志时&#xff0c;我们按照https://docs.abp.io/zh-Hans/abp/latest/Audit-Logging配置即可开箱即用&#xff0c;然而在实际生产环境中&#xff0c;某些配置并不可取&#xff0c;比如今天的主角——客户端IP&#xff0c;记录用…

    郭昶

    郭 昶左直拳饰演《外来媳妇本地郎》中康家老二康祁宗的演员郭昶6月14日去世了&#xff0c;胃癌&#xff0c;享年50岁。这个消息真令人难以置信&#xff0c;不胜嘘唏。 《外来媳妇本地郎》在广东这边很受欢迎&#xff0c;每集结尾那带有浓厚岭南特色的粤曲小调在胡同小巷时有…

    ABP vNext IOC替换原有Service实现

    即 .NET IOC替换原有Service实现背景在使用ABP vNext时&#xff0c;该框架为我们实现了非常多的默认行为&#xff0c;以便开箱即用&#xff0c;但在实际使用中&#xff0c;我们总是需要根据自己的需求定制自己的服务&#xff0c;在.Net框架中&#xff0c;便提供了Service.Repla…

    aqs java 简书,Java AQS源码解读

    1、先聊点别的说实话&#xff0c;关于AQS的设计理念、实现、使用&#xff0c;我有打算写过一篇技术文章&#xff0c;但是在写完初稿后&#xff0c;发现掌握的还是模模糊糊的&#xff0c;模棱两可。痛定思痛&#xff0c;脚踏实地重新再来一遍。这次以 Java 8源码为基础进行解读。…

    dnSpy反编译、部署调试神器

    一、概要在工作当中&#xff0c;当程序部署了之后就算打了日志遇到极个别的特殊异常没有在程序日志中体现出来或者没有详细的报错原因会让开发者非常头疼&#xff0c;不得不盲猜bug到底出在哪里。这里分享一下工作上经常会用到的工具&#xff0c;这款工具可以反编译并运行调试已…