搜索词>>route 耗时0.0020
  • Vue route路由跳转及传参

    一、如何在vue中使用route跳转页面并传递参数this.$router.push({ path: '/path/to/your',query:{param1:value1,param2:value2}});一、如何在vue中使用route跳转页面并传递参数this.$router.push({ path: '/path/to/your',query:{param1:value1,param2:value2}});
  • Centos6/7双网卡之修改默认路由

    Centos6/7双网卡之修改默认路由详解<h2>情景描述:</h2> 我们服务器有两个网卡,一个是内网网卡,一个是外网网卡;内网需要登陆公司账号才可以访问互联网;外网则不需要;但是默认的路由是内网;<br /> route列出所有路由: <pre> <code>route</code></pre> <br /> 删除默认的路由: <pre> <code>route del default //或者下面的 route del default gw 0.0.0.0</code></pre> 增加新的默认路由: <pre> <code>route add default gw 192.168.1.1 //或者下面的方式 route add -net 0.0.0.0 gw 192.168.1.1</code></pre> <br /> 上面的配置重启后会失效,解决方式是写入重启脚本/etc/rc.local<br /> <br />  
  • Vue.js底部导航三种写法

    普通写法​vantUI 使用字体图标和文字<template>l; <van-tabbar v-model="active" active-color="#07c160">l; <van-tabbar-item :to="{普通写法​vantUI 使用字体图标和文字<template> <van-tabbar v-model="active" active-color="#07c160"> <van-tabbar-item :to="{name:'home'}"> <span>首页</span> <img slot="icon" slot-scope="props" :src="props.active ? icon.homeActive : icon.homeNormal" > </van-tabbar-item> <van-tabbar-item :to="{name:'money'}"> <span>钱包</span> <img slot="icon" slot-scope="props" :src="props.active ? icon.moneyActive : icon.moneyNormal" > </van-tabbar-item> <van-tabbar-item :to="{name:'user'}"> <span>个人中心</span> <img slot="icon" slot-scope="props" :src="props.active ? icon.userActive : icon.userNormal" > </van-tabbar-item> </van-tabbar> </template> <script> export default { data() { return { active: 0, icon:{ homeNormal: require('./home.png'), homeActive: require('./homeActive.png'), moneyNormal: require('./money.png'), moneyActive: require('./moneyActive.png'), userNormal: require('./user.png'), userActive: require('./userActive.png'), }, } } } </script>手写 适用于背景图等复杂样式<template> <div class="footer"> <div class="foot"> <router-link v-for="(item,i) in footnavs" :key="i" :to="item.to" :class="{'selected':($route.meta.nav_index==item.nav_index)}" > <img :src="$route.meta.nav_index==item.nav_index?item.selected:item.no_selected" alt> <span v-show="!($route.meta.nav_index==item.nav_index)">{{item.name}}</span> </router-link> </div> </div> </template> <script> export default { data() { return { footnavs: [ { to: "/", name: "首页", no_selected: require("@img/home/shouye@2x.png"), selected: require("@img/home/shouye2@2x.png"), nav_index: 1 }, { to: "/money", name: "钱包", no_selected: require("@img/home/qianbao@2x.png"), selected: require("@img/home/qianbao2@2x.png"), nav_index: 2 }, { to: "/my", name: "个人中心", no_selected: require("@img/home/gerenhzongxin@2x.png"), selected: require("@img/home/gerenzhongxin@2x.png"), nav_index: 3 } ] }; } }; </script> <style lang="less" scoped> .footer { width: 100%; height: 98px; margin-top: 12px; .foot { width: 100%; position: fixed; left: 0; right: 0; bottom: 0; display: flex; justify-content: space-between; padding: 10px 20px; background: rgba(255, 255, 255, 1); box-shadow: 0px 1px 16px 0px rgba(192, 192, 192, 0.4); a { width: 33%; text-align: center; display: flex; flex-direction: column; justify-content: space-around; align-items: center; img { width: 47px; height: 47px; } span { font-size: 28px; color: rgba(102, 102, 102, 1); } &.selected { img { width: 85px; height: 85px; } } } } } </style>路由文件import Vue from 'vue' import Router from 'vue-router' import home from '@/pages/home/home' import noticeList from '@/pages/home/noticeList' import money from '@/pages/money/money' import my from '@/pages/my/my' Vue.use(Router) export default new Router({ routes: [{ path: '/', name: 'home', component: home, meta: { index: 0, hasFooter: true, nav_index: 1 }, }, { path: '/money', name: 'money', component: money, meta: { index: 0, hasFooter: true, nav_index: 2 }, }, { path: '/my', name: 'my', component: my, meta: { index: 0, hasFooter: true, nav_index: 3 }, }, { path: '/noticeList', name: 'noticeList', component: noticeList, meta: { index: 1, hasBar: true, }, }, ], //该方法用于控制滚动行为, scrollBehavior(to, form, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } } })app.vue文件<template> <div id="app"> <tabBar v-show="$route.meta.hasBar"/> <transition :name="transitionName"> <router-view/> </transition> <Footer v-show="$route.meta.hasFooter"/> </div> </template> <script> import "./common/css/common.css"; // bug:不能为小写footer会报错 import Footer from "@/components/footer"; import tabBar from "@/components/tabBar"; export default { name: "App", data() { return { transitionName: "" }; }, components: { Footer, tabBar }, mounted() {}, watch: { /* * 使用watch 监听$router的变化 * 如果to索引大于from索引,判断为前进状态,反之则为后退状态 * 设置动画名称 */ $route(to, from) { if (to.meta.index == from.meta.index) { this.transitionName = ""; } else if (to.meta.index > from.meta.index) { this.transitionName = "slide-left"; } else { this.transitionName = "slide-right"; } } } }; </script>​​​​​​​
  • Vue 登录后返回上一页完美解决办法

    Vue 如何返回上一页(上一个锚点)//...省略 methods:{ goback:function(){ this.$router.go(-1);//返回上一页 } } //...省略注意Vue 如何返回上一页(上一个锚点)//...省略 methods:{ goback:function(){ this.$router.go(-1);//返回上一页 } } //...省略注意:前提是使用了Vue 的route组件
  • Spring WebFlux 项目实战 在Spring WebFlux中创建多个RouterFunctions

    Spring WebFlux 项目实战 在Spring WebFlux中创建多个RouterFunctions,在这篇文章中,我们将着眼于在Spring WebFlux中将多个路由器功能定义到不同的逻辑域。<h2>引言</h2> <blockquote> <p>在这篇文章中,我们将着眼于在Spring WebFlux中将多个路由器功能定义到不同的逻辑域。如果您创建“微服务”,这可能不会成为问题,因为您很可能只在每个服务的单个域中工作,但如果您不是,那么您可能需要在应用程序中包含多个域用户或您自己的服务可以与之交互。这样做的代码就像我希望的那样简单,可以用几句话来解释。为了使这篇文章更有趣一些,我们将看一些使这一切成为可能的Spring代码。</p> </blockquote> 如果你是WebFlux的新手,我推荐看看我以前的博客,[<a rel="" target="_blank"href="http://www.leftso.com/blog/379.html" rel="" target="_blank">Spring WebFlux 项目实战</a>],在那里我写了一些关于这个主题的完整例子和解释。<br />   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">所以我们先假设置场景。你的应用程序中有两个不同的域,例如人员和位置。你可能想要让它们不仅在逻辑上而且在你的代码中彼此分离。要做到这一点,您需要一种方法来定义与其他域相互隔离的路由。这是我们将在这篇文章中看到的内容。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">如果你认为你已经知道这个问题的答案,那么你可能是对的。这真的很简单。尽管如此,让我们继续努力吧。要为人员域创建路由,请创建一个<code>RouterFunction</code>映射到相关处理函数的bean,如下所示。</span></span></span></p> <pre> <code class="language-java">@Configuration public class MyRouter { // works for a single bean @Bean public RouterFunction<ServerResponse> routes(PersonHandler personHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry); } }</code></pre> <br /> 这将创建到各种处理函数的路由<code>PersonHandler</code>。<br /> 所以,现在我们要添加位置逻辑的路由。我们可以简单地将路由添加到此bean,如下所示。 <pre> <code class="language-java">@Configuration public class MyRouter { // not ideal! @Bean public RouterFunction<ServerResponse> routes(PersonHandler personHandler, LocationHandler locationHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry) .andRoute(GET("/locations/{id}").and(accept(APPLICATION_JSON)), locationHandler::get); } }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这个bean现在包含一个引用,<code>LocationHandler</code>以便可以设置位置路由。这个解决方案的问题是它需要将代码耦合在一起。而且,如果你需要添加更多的处理程序,你很快就会被注入到这个bean中的依赖关系的数量所淹没。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">解决这个问题的方法是创建多个<code>RouterFunction</code>bean。而已。因此,如果我们在人员域中创建一个,<code>PersonRouter</code>并且在位置域中指定一个<code>LocationRouter</code>,则每个人都可以定义他们需要的路由,而Spring将完成剩下的任务。这是有效的,因为Spring遍历应用程序上下文并找到或创建任何<code>RouterFunction</code>bean并将它们合并为一个函数供以后使用。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">使用这些信息,我们可以编写下面的代码。</span></span></span><br />  </p> <pre> <code class="language-java">@Configuration public class PersonRouter { // solution @Bean public RouterFunction<ServerResponse> peopleRoutes(PersonHandler personHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry); } }</code></pre> 和 <pre> <code class="language-java">@Configuration public class LocationRouter { // solution @Bean public RouterFunction<ServerResponse> locationRoutes(LocationHandler locationHandler) { return RouterFunctions.route(GET("/locations/{id}").and(accept(APPLICATION_JSON)), locationHandler::get); } }</code></pre> <code>PersonRouter</code>可以与其他人/与人相关的代码保存,并<code>LocationRouter</code>可以做同样的事情。<br />   <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">为了让这个更有趣,为什么这个工作?</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff"><code>RouterFunctionMapping</code>是检索<code>RouterFunction</code>应用程序上下文中创建的所有bean 的类。该<code>RouterFunctionMapping</code>豆创建中<code>WebFluxConfigurationSupport</code>这是春天WebFlux配置震中。通过<code>@EnableWebFlux</code>在配置类中包含注释或者依靠自动配置,一系列事件开始并收集我们所有的<code>RouterFunction</code>s就是其中之一。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">下面是这<code>RouterFunctionMapping</code>堂课。我已经删除了它的构造函数和一些方法来使这里的代码片段更容易消化。</span></span></span></p> <pre> <code class="language-java">public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean { @Nullable private RouterFunction<?> routerFunction; private List<HttpMessageReader<?>> messageReaders = Collections.emptyList(); // constructors // getRouterFunction // setMessageReaders @Override public void afterPropertiesSet() throws Exception { if (CollectionUtils.isEmpty(this.messageReaders)) { ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create(); this.messageReaders = codecConfigurer.getReaders(); } if (this.routerFunction == null) { initRouterFunctions(); } } /** * Initialized the router functions by detecting them in the application context. */ protected void initRouterFunctions() { if (logger.isDebugEnabled()) { logger.debug("Looking for router functions in application context: " + getApplicationContext()); } List<RouterFunction<?>> routerFunctions = routerFunctions(); if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) { routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction)); } this.routerFunction = routerFunctions.stream() .reduce(RouterFunction::andOther) .orElse(null); } private List<RouterFunction<?>> routerFunctions() { SortedRouterFunctionsContainer container = new SortedRouterFunctionsContainer(); obtainApplicationContext().getAutowireCapableBeanFactory().autowireBean(container); return CollectionUtils.isEmpty(container.routerFunctions) ? Collections.emptyList() : container.routerFunctions; } // getHandlerInternal private static class SortedRouterFunctionsContainer { @Nullable private List<RouterFunction<?>> routerFunctions; @Autowired(required = false) public void setRouterFunctions(List<RouterFunction<?>> routerFunctions) { this.routerFunctions = routerFunctions; } } }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">检索所有路由的路径开始于创建bean <code>afterPropertiesSet</code>后调用<code>RouterFunctionMapping</code>。由于它是内部的<code>RouterFunction</code>,<code>null</code>它会<code>initRouterFunctions</code>触发一系列导致执行的方法<code>routerFunctions</code>。一个新<code>SortedRouterFunctionsContainer</code>的构造(私有静态类)<code>routerFunctions</code>通过注入<code>RouterFunction</code>应用程序上下文中的所有s来设置它的字段。这工作,因为Spring会注入类型的所有豆类<code>T</code>一个当<code>List<T></code>被注入。现在检索到的<code>RouterFunction</code>s被组合在一起以制作一个<code>RouterFunction</code>从现在开始用于将所有传入请求路由到适当处理程序的单个节点。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这里的所有都是它的。总而言之,<code>RouterFunction</code>为不同业务领域定义多个是非常简单的,因为您只需在最有意义的任何区域创建它们,Spring就会关闭并获取所有区域。为了使我们研究的一些魔法神秘化,<code>RouterFunctionMapping</code>以了解<code>RouterFunction</code>我们创建的s是如何收集和组合的,以便它们可以用来将请求路由到处理程序。作为结束语,我确实明白这篇文章在某些方面是相当微不足道的,但有时看起来很明显的信息会非常有帮助。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我建议看看我以前的帖子[<a rel="" target="_blank"href="http://www.leftso.com/blog/379.html" rel="" target="_blank">Spring WebFlux 项目实战</a>]</span></span></span></p>
  • spring boot RabbitMQ基本集成使用

    spring boot RabbitMQ基本集成使用spring boot RabbitMQ基本集成使用 <h2>RabbitMQ介绍</h2> <p>RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。RabbitMQ主要是为了实现系统之间的双向解耦而实现的。当生产者大量产生数据时,消费者无法快速消费,那么需要一个中间层。保存这个数据。</p> <p>AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。</p> <p>RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。</p> <h2>相关概念</h2> 通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。<br /> <img alt="" class="img-thumbnail" src="/assist/images/blog/c3db0eeb8a2d40d3a9a1437c424b9934.jpg" /> <p>左侧 P 代表 生产者,也就是往 RabbitMQ 发消息的程序。</p> <ul> <li> <p>中间即是 RabbitMQ,<em>其中包括了 交换机 和 队列。</em></p> </li> <li> <p>右侧 C 代表 消费者,也就是往 RabbitMQ 拿消息的程序。</p> </li> </ul> <p>那么,<em>其中比较重要的概念有 4 个,分别为:虚拟主机,交换机,队列,和绑定。</em></p> <ul> <li> <p>虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单,RabbitMQ当中,<em>用户只能在虚拟主机的粒度进行权限控制。</em> 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。</p> </li> <li> <p>交换机:<em>Exchange 用于转发消息,但是它不会做存储</em> ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。<br /> 这里有一个比较重要的概念:<strong>路由键</strong> 。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。</p> </li> <li> <p>绑定:也就是交换机需要和队列相绑定,这其中如上图所示,是多对多的关系。</p> </li> </ul> <h3>交换机(Exchange)</h3> <p>交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout</p> <ul> <li> <p>Direct:direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 <strong>routing_key</strong>, 消息的<strong>routing_key</strong> 匹配时, 才会被交换器投送到绑定的队列中去.</p> </li> <li> <p>Topic:按规则转发消息(最灵活)</p> </li> <li> <p>Headers:设置header attribute参数类型的交换机</p> </li> <li> <p>Fanout:转发消息到所有绑定队列</p> </li> </ul> <p><strong>Direct Exchange</strong><br /> Direct  Exchange是RabbitMQ默认的交换机模式,也是最简单的模式,根据key全文匹配去寻找队列。<br /> <img alt="生产消费关系图" class="img-thumbnail" src="/assist/images/blog/a50800a7e550419ebed551dd4f2cd5aa.png" /><br />  </p> <p>第一个 X - Q1 就有一个 binding key,名字为 orange; X - Q2 就有 2 个 binding key,名字为 black 和 green。<em>当消息中的 路由键 和 这个 binding key 对应上的时候,那么就知道了该消息去到哪一个队列中。</em></p> <p>Ps:为什么 X 到 Q2 要有 black,green,2个 binding key呢,一个不就行了吗? - 这个主要是因为可能又有 Q3,而Q3只接受 black 的信息,而Q2不仅接受black 的信息,还接受 green 的信息。</p> <p><strong>Topic Exchange</strong>  </p> <p><em>Topic Exchange 转发消息主要是根据通配符。</em> 在这种交换机下,队列和交换机的绑定会定义一种路由模式,那么,通配符就要在这种路由模式和路由键之间匹配后交换机才能转发消息。</p> <p>在这种交换机模式下:</p> <ul> <li> <p>路由键必须是一串字符,用句号(<code>.</code>) 隔开,比如说 agreements.us,或者 agreements.eu.stockholm 等。</p> </li> <li> <p>路由模式必须包含一个 星号(<code>*</code>),主要用于匹配路由键指定位置的一个单词,比如说,一个路由模式是这样子:agreements..b.*,那么就只能匹配路由键是这样子的:第一个单词是 agreements,第四个单词是 b。 井号(#)就表示相当于一个或者多个单词,例如一个匹配模式是agreements.eu.berlin.#,那么,以agreements.eu.berlin开头的路由键都是可以的。</p> </li> </ul> <p>具体代码发送的时候还是一样,第一个参数表示交换机,第二个参数表示routing key,第三个参数即消息。如下:<br />  </p> <pre> <code class="language-html">rabbitTemplate.convertAndSend("testTopicExchange","key1.a.c.key2", " this is RabbitMQ!");</code></pre> <p>topic 和 direct 类似, 只是匹配上支持了”模式”, 在”点分”的 routing_key 形式中, 可以使用两个通配符:</p> <ul> <li> <p><code>*</code>表示一个词.</p> </li> <li> <p><code>#</code>表示零个或多个词.</p> </li> </ul> <p><strong>Headers Exchange</strong></p> <p>headers 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型.<br /> 在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列.</p> <p><strong>Fanout Exchange</strong></p> <p>Fanout Exchange 消息广播的模式,不管路由键或者是路由模式,<em>会把消息发给绑定给它的全部队列</em>,如果配置了routing_key会被忽略。</p> <h2>springboot集成RabbitMQ</h2> springboot集成RabbitMQ非常简单,如果只是简单的使用配置非常少,springboot提供了spring-boot-starter-amqp项目对消息各种支持。 <h3>简单使用</h3> 1、配置pom包,主要是添加spring-boot-starter-amqp的支持 <pre> <code class="language-xml"><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>x</code></pre> <p>2、配置文件</p> <p>配置rabbitmq的安装地址、端口以及账户信息<br />  </p> <pre> <code class="language-html">spring.application.name=spirng-boot-rabbitmq spring.rabbitmq.host=192.168.0.86 spring.rabbitmq.port=5672 spring.rabbitmq.username=admin spring.rabbitmq.password=123456</code></pre> 3、队列配置 <pre> <code class="language-java">@Configuration public class RabbitConfig { @Bean public Queue Queue() { return new Queue("hello"); } }</code></pre> <p>4、发送者</p> <p>rabbitTemplate是springboot 提供的默认实现<br />  </p> <pre> <code class="language-java">public class HelloSender { @Autowired private AmqpTemplate rabbitTemplate; public void send() { String context = "hello " + new Date(); System.out.println("Sender : " + context); this.rabbitTemplate.convertAndSend("hello", context); } }</code></pre> 5、接收者 <pre> <code class="language-java">@Component @RabbitListener(queues = "hello") public class HelloReceiver { @RabbitHandler public void process(String hello) { System.out.println("Receiver : " + hello); } }</code></pre> 6、测试 <pre> <code class="language-java">@RunWith(SpringRunner.class) @SpringBootTest public class RabbitMqHelloTest { @Autowired private HelloSender helloSender; @Test public void hello() throws Exception { helloSender.send(); } }</code></pre> <blockquote> <p>注意,发送者和接收者的queue name必须一致,不然不能接收</p> </blockquote> <h3>多对多使用</h3> <p>一个发送者,N个接收者或者N个发送者和N个接收者会出现什么情况呢?</p> <p><strong>一对多发送</strong><br /> 对上面的代码进行了小改造,接收端注册了两个Receiver,Receiver1和Receiver2,发送端加入参数计数,接收端打印接收到的参数,下面是测试代码,发送一百条消息,来观察两个接收端的执行效果<br />  </p> <pre> <code class="language-java">@Test public void oneToMany() throws Exception { for (int i=0;i<100;i++){ neoSender.send(i); } }</code></pre> 结果如下: <pre> <code class="language-html">Receiver 1: spirng boot neo queue ****** 11 Receiver 2: spirng boot neo queue ****** 12 Receiver 2: spirng boot neo queue ****** 14 Receiver 1: spirng boot neo queue ****** 13 Receiver 2: spirng boot neo queue ****** 15 Receiver 1: spirng boot neo queue ****** 16 Receiver 1: spirng boot neo queue ****** 18 Receiver 2: spirng boot neo queue ****** 17 Receiver 2: spirng boot neo queue ****** 19 Receiver 1: spirng boot neo queue ****** 20</code></pre> <br /> 根据返回结果得到以下结论 <blockquote> <p>一个发送者,N个接受者,经过测试会均匀的将消息发送到N个接收者中</p> </blockquote> <p><strong>多对多发送</strong>  </p> <p>复制了一份发送者,加入标记,在一百个循环中相互交替发送<br />  </p> <pre> <code class="language-java">@Test public void manyToMany() throws Exception { for (int i=0;i<100;i++){ neoSender.send(i); neoSender2.send(i); } }</code></pre> 结果如下: <pre> <code class="language-html">Receiver 1: spirng boot neo queue ****** 20 Receiver 2: spirng boot neo queue ****** 20 Receiver 1: spirng boot neo queue ****** 21 Receiver 2: spirng boot neo queue ****** 21 Receiver 1: spirng boot neo queue ****** 22 Receiver 2: spirng boot neo queue ****** 22 Receiver 1: spirng boot neo queue ****** 23 Receiver 2: spirng boot neo queue ****** 23 Receiver 1: spirng boot neo queue ****** 24 Receiver 2: spirng boot neo queue ****** 24 Receiver 1: spirng boot neo queue ****** 25 Receiver 2: spirng boot neo queue ****** 25</code></pre> <blockquote> <p>结论:和一对多一样,接收端仍然会均匀接收到消息</p> </blockquote> <h3>高级使用</h3> <p><strong>对象的支持</strong></p> <p>springboot以及完美的支持对象的发送和接收,不需要格外的配置。<br />  </p> <pre> <code class="language-java">//发送者 public void send(User user) { System.out.println("Sender object: " + user.toString()); this.rabbitTemplate.convertAndSend("object", user); } ... //接收者 @RabbitHandler public void process(User user) { System.out.println("Receiver object : " + user); }</code></pre> 结果如下: <pre> <code class="language-html">Sender object: User{name='neo', pass='123456'} Receiver object : User{name='neo', pass='123456'}</code></pre> <p><strong>Topic Exchange</strong></p> <p>topic 是RabbitMQ中最灵活的一种方式,可以根据routing_key自由的绑定不同的队列</p> <p>首先对topic规则配置,这里使用两个队列来测试<br />  </p> <pre> <code class="language-java">@Configuration public class TopicRabbitConfig { final static String message = "topic.message"; final static String messages = "topic.messages"; @Bean public Queue queueMessage() { return new Queue(TopicRabbitConfig.message); } @Bean public Queue queueMessages() { return new Queue(TopicRabbitConfig.messages); } @Bean TopicExchange exchange() { return new TopicExchange("exchange"); } @Bean Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) { return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message"); } @Bean Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) { return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#"); } }</code></pre> 使用queueMessages同时匹配两个队列,queueMessage只匹配”topic.message”队列 <pre> <code class="language-java">public void send1() { String context = "hi, i am message 1"; System.out.println("Sender : " + context); this.rabbitTemplate.convertAndSend("exchange", "topic.message", context); } public void send2() { String context = "hi, i am messages 2"; System.out.println("Sender : " + context); this.rabbitTemplate.convertAndSend("exchange", "topic.messages", context); }</code></pre> <p>发送send1会匹配到topic.#和topic.message 两个Receiver都可以收到消息,发送send2只有topic.#可以匹配所有只有Receiver2监听到消息</p> <p><strong>Fanout Exchange</strong></p> <p>Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。</p> <p>Fanout 相关配置<br />  </p> <pre> <code class="language-java">@Configuration public class FanoutRabbitConfig { @Bean public Queue AMessage() { return new Queue("fanout.A"); } @Bean public Queue BMessage() { return new Queue("fanout.B"); } @Bean public Queue CMessage() { return new Queue("fanout.C"); } @Bean FanoutExchange fanoutExchange() { return new FanoutExchange("fanoutExchange"); } @Bean Binding bindingExchangeA(Queue AMessage,FanoutExchange fanoutExchange) { return BindingBuilder.bind(AMessage).to(fanoutExchange); } @Bean Binding bindingExchangeB(Queue BMessage, FanoutExchange fanoutExchange) { return BindingBuilder.bind(BMessage).to(fanoutExchange); } @Bean Binding bindingExchangeC(Queue CMessage, FanoutExchange fanoutExchange) { return BindingBuilder.bind(CMessage).to(fanoutExchange); } }</code></pre> 这里使用了A、B、C三个队列绑定到Fanout交换机上面,发送端的routing_key写任何字符都会被忽略: <pre> <code class="language-java">public void send() { String context = "hi, fanout msg "; System.out.println("Sender : " + context); this.rabbitTemplate.convertAndSend("fanoutExchange","", context); }</code></pre> 结果如下: <pre> <code class="language-html">Sender : hi, fanout msg ... fanout Receiver B: hi, fanout msg fanout Receiver A : hi, fanout msg fanout Receiver C: hi, fanout msg</code></pre> <blockquote> <p>结果说明,绑定到fanout交换机上面的队列都收到了消息</p> </blockquote>
  • linux中samba客服端smbclient整合shell脚本

    linux中samba客服端smbclient整合shell脚本实现类似ftp脚本下载上传文件,Linux,samba,smbclientlinux中samba客服端smbclient整合shell脚本实现类似ftp脚本下载上传文件<br />   <pre> <code class="language-bash">#!/bin/bash localDir=/smb/media #smbclient //10.1.1.1/XiaoMi-usb0 -U guest%1 #其中上面的用户名和密码是guest%1 用户是guest 密码是1,链接操作 smbclient //10.1.1.1/XiaoMi-usb0 -U guest%1 <<- EOF #切换本地目录,类似ftp lcd $localDir #切换远程目录 cd 下载 prompt #下载需要下载的文件或匹配文件 mget $1 exit EOF</code></pre> <br /> 执行: <pre> <code class="language-bash">./download-route-samba.sh *速度与激情*.mkv</code></pre> <br /> 执行脚本将会把满足条件的文件全部download到本地。<br /> <br /> 脚本中的很多参数可以根据自身需要进行修改。<br /> <br /> 至次 samba客服端smbclient整合shell脚本实现脚本批处理自动处理等功能。<br /> <br />  
  • vue-router 中 routers 定义写法

    vue-router 中 routers 定义写法,讨论 require 的使用与否​首先上 router的index.jsimport Vue from 'vue' import Router from 'vue-router' //首页vue-router 中 routers 定义写法,讨论 require 的使用与否​首先上 router的index.jsimport Vue from 'vue' import Router from 'vue-router' //首页 //import index from '@/components/index' //产品中心 import productCenter from '@/components/productCenter/productCenter.vue' //商务合作 import teamwork from '@/components/teamwork/teamwork.vue' //咨询中心 import askCenter from '@/components/askCenter/askCenter.vue' //关于中棋 import aboutUs from '@/components/aboutUs/aboutUs.vue' Vue.use(Router) export default new Router({ routes: [ //首页 { path: '/', name: 'index', component:resolve =>require(['index'],resolve) } //产品中心 ,{ path: '/productCenter', name: 'productCenter', component: productCenter } //商务合作 ,{ path: '/teamwork', name: 'teamwork', component: teamwork } //咨询中心 ,{ path: '/askCenter', name: 'askCenter', component: askCenter } //关于我们 ,{ path: '/aboutUs', name: 'aboutUs', component: aboutUs } ] }) 重点说明:提示:import 引入时用到的“@”就相当于“..”;我们看到index页面是用的require的方式写的路由,所以上面的import就注释掉了,这种写法的好处,不仅仅是简单,还有这样写是按需加载,访问此路由时才加载这个js,会避免进入首页时加载内容过多,因为import引入,当项目打包时路由里的所有component都会打包在一个js中,而用require会将你的component分别打包成不同的js。最后总结:推荐使用require 方式编写routers,按需加载节约资源。 图像 小部件
  • docker常见问题汇总

    启动docker web服务时 虚拟机端口转发 外部无法访问,WARNING: IPv4 forwarding is disabled. Networking will not work.<h2>问题:WARNING: IPv4 forwarding is disabled. Networking will not work.</h2> <p>centos 7 docker 启动了一个web服务 但是启动时 报</p> <p>WARNING: IPv4 forwarding is disabled. Networking will not work.</p> <p>网上查询了下 需要做如下配置</p> <p> </p> 解决办法: <pre> <code class="language-html"># vi /etc/sysctl.conf</code></pre> <br /> 或者 <pre> <code class="language-html"># vi /usr/lib/sysctl.d/00-system.conf</code></pre> <br /> 添加如下代码:<br />     <pre> <code class="language-html">net.ipv4.ip_forward=1</code></pre> <br /> 重启network服务 <pre> <code class="language-html"># systemctl restart network</code></pre> <br /> 查看是否修改成功 <pre> <code class="language-html"># sysctl net.ipv4.ip_forward</code></pre> 如果返回为“net.ipv4.ip_forward = 1”则表示成功了 <h2>问题:docker telnet no route to host</h2> 引起原因:docker容器之间通过主机IP+主机端口相互通讯<br /> 解决方法:<br /> 检查主机防火墙规则,如果防火墙开着开放需要通讯的端口<br /> 参考:<a rel="" target="_blank"href="http://www.leftso.com/blog/163.html" rel="" target="_blank">centos7 防火墙开放端口</a> <h2>问题:docker 容器内部无法访问外网</h2> 引起原因:启动docker服务的时候防火墙服务(firewalld)未启动。docker服务启动后再启动firewalld服务导致无法访问外网。<br /> 解决办法:firewall启动的时候重启一下docker服务。然后在启动里面的容器
  • Spring WebFlux 项目实战 spring boot 2.0正式版

    引言Spring Boot 2.0最近去了GA,所以我决定写我关于Spring的第一篇文章很长一段时间引言Spring Boot 2.0最近去了GA,所以我决定写我关于Spring的第一篇文章很长一段时间。自发布以来,我一直在看到越来越多的Spring WebFlux以​​及如何使用它的教程。但是在阅读完它们并尝试让它自己工作之后,我发现从包含在我阅读的文章和教程中的代码跳转到编写实际上比返回字符串更有趣的事情从后端。现在,我希望我不会在自己的脚下说自己可能会对我在这篇文章中使用的代码做出同样的批评,但这里是我试图给Spring WebFlux的教程,它实际上类似于你可能会在野外使用的东西。项目结构:​在我继续之前,在提及WebFlux之后,究竟是什么呢?Spring WebFlux是Spring MVC的完全非阻塞反应式替代方案。它允许更好的垂直缩放而不增加硬件资源。被动反应它现在使用Reactive Streams来允许从调用返回到服务器的数据的异步处理。这意味着我们将看到更少的Lists,Collection甚至单个对象,而不是他们的反应等价物,例如Flux和Mono(来自Reactor)。我不会深入研究Reactive Streams是什么,诚实地说,在我尝试向任何人解释它之前,我需要更加深入地研究它。相反,让我们回过头来关注WebFlux。像往常一样,我使用Spring Boot在本教程中编写代码。以下是我在这篇文章中使用的依赖关系。<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId> <version>2.0.0.RELEASE</version> </dependency> </dependencies>尽管我没有将它包含在上面的依赖代码片段中,但是它spring-boot-starter-parent被使用了,最终可以将其提升到版本2.0.0.RELEASE。本教程是关于WebFlux的,包括这spring-boot-starter-webflux显然是一个好主意。spring-boot-starter-data-cassandra-reactive也被包括在内,因为我们将用它作为示例应用程序的数据库,因为它是少数几个有反应支持的数据库之一(在编写本文时)。通过一起使用这些依赖关系,我们的应用程序可以从前到后完全反应。WebFlux引入了一种不同的方式来处理请求,而不是使用Spring MVC 中使用的@Controller或@RestController编程模型。但是,它并没有取代它。相反,它已被更新以允许使用被动类型。这使您可以保持与使用Spring编写相同的格式,但对返回类型进行一些更改,以便返回Fluxs或Monos。下面是一个非常人为的例子。@RestController public class PersonController { private final PersonRepository personRepository; public PersonController(PersonRepository personRepository) { this.personRepository = personRepository; } @GetMapping("/people") public Flux<Person> all() { return personRepository.findAll(); } @GetMapping("/people/{id}") Mono<Person> findById(@PathVariable String id) { return personRepository.findOne(id); } }对我来说,这看起来非常熟悉,并且从一眼就可以看出它与标准的Spring MVC控制器没有任何区别,但通过阅读方法后,我们可以看到不同的返回类型。在这个例子中PersonRepository必须是一个被动库,因为我们已经能够直接返回他们的搜索查询的结果供参考,被动库会返回一个Flux集合和一个Mono单一的实体。注释方法不是我想在这篇文章中关注的内容。这对我们来说不够酷,时髦。没有足够的lambda表达式来满足我们以更有效的方式编写Java的渴望。但Spring WebFlux有我们的支持。它提供了一种替代方法来路由和处理请求到我们的服务器,轻轻地使用lambdas编写路由器功能。我们来看一个例子。@Configuration public class PersonRouter { @Bean public RouterFunction<ServerResponse> route(PersonHandler personHandler) { return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get) .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all) .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post) .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put) .andRoute(DELETE("/people/{id}"), personHandler::delete) .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry); } } 这些都是PersonHandler我们稍后会看到的方法的所有路线。我们创建了一个将处理我们路由的bean。为了设置路由功能,我们使用了名为的RouterFunctions类为我们提供了一个静态方法,但现在我们只关心它的route方法。以下是该route方法的签名。public static <T extends ServerResponse> RouterFunction<T> route( RequestPredicate predicate, HandlerFunction<T> handlerFunction) { // stuff }  该方法表明,它与a RequestPredicate一起HandlerFunction并输出a RouterFunction。这RequestPredicate是我们用来指定路由的行为,比如我们处理函数的路径,它是什么类型的请求以及它可以接受的输入类型。由于我使用静态导入将所有内容读得更清晰,所以一些重要信息已经隐藏起来。要创建一个RequestPredicate我们应该使用RequestPredicates(复数),一个静态帮助类为我们提供我们需要的所有方法。就个人而言,我建议静态导入,RequestPredicates否则由于使用RequestPredicates静态方法可能需要的次数,您的代码将会一团糟。在上述例子中,GET,POST,PUT,DELETE,accept和contentType都是静态RequestPredicates方法。下一个参数是a HandlerFunction,它是一个功能接口。这里有三件重要的信息,它有一个泛型类型<T extends ServerResponse>,它的handle方法返回一个Mono<T>并且需要一个ServerRequest。使用这些我们可以确定我们需要传递一个返回一个Mono<ServerResponse>(或它的一个子类型)的函数。这显然对我们的处理函数返回的内容有严格的约束,因为它们必须满足这个要求,否则它们将不适合以这种格式使用。最后的结果是一个RouterFunction。这可以返回并用于路由到我们指定的任何函数。但通常情况下,我们希望一次将很多不同的请求发送给各种处理程序,这是WebFlux迎合的。由于route返回a RouterFunction以及RouterFunction也有其自己的路由方法的事实andRoute,我们可以将这些调用链接在一起并继续添加我们所需的所有额外路由。如果我们再回头看一下PersonRouter上面的例子,我们可以看到这些方法是以REST动词命名的,例如GET,POST它们定义了处理程序将要执行的请求的路径和类型。GET例如,如果我们以第一个请求为例,它将/people使用路径变量名称id(path表示的路径变量{id})和返回内容的类型(具体来说,使用该方法定义的APPLICATION_JSON静态字段from MediaType)进行路由accept。如果使用不同的路径,则不会被处理。如果路径正确但Accept头不是可接受的类型之一,则请求将失败。在我们继续之前,我想了解一下accept和contentType方法。这两个设置请求标头都accept与Accept标头和contentTypeContent-Type 匹配。Accept头定义了响应可接受的媒体类型,因为我们返回的Person对象的JSON表示设置为APPLICATION_JSON(application/json在实际头文件中)是有意义的。Content-Type具有相同的想法,但是却描述了发送请求正文内的媒体类型。这就是为什么只有动词POST和PUT动词才contentType包括在内,因为其他人在他们的身体中没有任何东西。DELETE不包括accept和contentType 所以我们可以得出这样的结论:它既没有期望返回任何东西,也没有在其请求中包含任何东西。现在我们知道如何设置路由,让我们看看如何编写处理传入请求的处理程序方法。以下是处理前面示例中定义的路由的所有请求的代码。@Component public class PersonHandler { private final PersonManager personManager; public PersonHandler(PersonManager personManager) { this.personManager = personManager; } public Mono<ServerResponse> get(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); final Mono<Person> person = personManager.findById(id); return person .flatMap(p -> ok().contentType(APPLICATION_JSON).body(fromPublisher(person, Person.class))) .switchIfEmpty(notFound().build()); } public Mono<ServerResponse> all(ServerRequest request) { return ok().contentType(APPLICATION_JSON) .body(fromPublisher(personManager.findAll(), Person.class)); } public Mono<ServerResponse> put(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); final Mono<Person> person = request.bodyToMono(Person.class); return personManager .findById(id) .flatMap( old -> ok().contentType(APPLICATION_JSON) .body( fromPublisher( person .map(p -> new Person(p, id)) .flatMap(p -> personManager.update(old, p)), Person.class))) .switchIfEmpty(notFound().build()); } public Mono<ServerResponse> post(ServerRequest request) { final Mono<Person> person = request.bodyToMono(Person.class); final UUID id = UUID.randomUUID(); return created(UriComponentsBuilder.fromPath("people/" + id).build().toUri()) .contentType(APPLICATION_JSON) .body( fromPublisher( person.map(p -> new Person(p, id)).flatMap(personManager::save), Person.class)); } public Mono<ServerResponse> delete(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); return personManager .findById(id) .flatMap(p -> noContent().build(personManager.delete(p))) .switchIfEmpty(notFound().build()); } public Mono<ServerResponse> getByCountry(ServerRequest serverRequest) { final String country = serverRequest.pathVariable("country"); return ok().contentType(APPLICATION_JSON) .body(fromPublisher(personManager.findAllByCountry(country), Person.class)); } }  有一点非常明显,就是缺少注释。酒吧的@Component注释自动创建一个PersonHandler豆没有其他Spring注解。我试图将大部分存储库逻辑保留在这个类之外,并且通过经由它所包含的PersonManager代理来隐藏对实体对象的任何引用PersonRepository。如果你对代码感兴趣,PersonManager那么可以在我的GitHub上看到,关于它的进一步解释将被排除在这篇文章之外,所以我们可以专注于WebFlux本身。好的,回到手头的代码。让我们仔细看看get和post方法来弄清楚发生了什么。public Mono<ServerResponse> get(ServerRequest request) { final UUID id = UUID.fromString(request.pathVariable("id")); final Mono<Person> person = personManager.findById(id); return person .flatMap(p -> ok().contentType(APPLICATION_JSON).body(fromPublisher(person, Person.class))) .switchIfEmpty(notFound().build()); }此方法用于从支持此示例应用程序的数据库中检索单个记录。由于Cassandra是选择的数据库,我决定使用UUID每个记录的主键,这使得测试示例更令人讨厌的不幸效果,但没有任何复制和粘贴无法解决的问题。请记住,此GET请求的路径中包含路径变量。使用pathVariable的方法ServerRequest传递到我们能够提取它的价值通过提供变量的名称,在这种情况下,方法id。然后将ID转换成一个UUID,如果字符串格式不正确,它会抛出一个异常,我决定忽略这个问题,所以示例代码不会变得混乱。一旦我们有了ID,我们就可以查询数据库中是否存在匹配的记录。Mono<Person>返回的A 包含映射到a的现有记录,Person或者它保留为空Mono。使用返回的,Mono我们可以根据它的存在输出不同的响应。这意味着我们可以将有用的状态代码返回给客户端以跟随主体的内容。如果记录存在,则flatMap返回一个ServerResponse与OK状态。伴随着这种状态,我们希望输出记录,为此,我们在这种情况下指定正文的内容类型APPLICATION_JSON,并将记录添加到记录中。fromPublisher需要我们Mono<Person>(这是一个Publisher)与Person课程一起,因此它知道它映射到身体中的是什么。fromPublisher是类的静态方法BodyInserters。如果记录不存在,那么流程将移动到switchIfEmpty块中并返回NOT FOUND状态。因为没有发现,身体可以留空,所以我们只是创建ServerResponse那里。现在到post处理程序。public Mono<ServerResponse> post(ServerRequest request) { final Mono<Person> person = request.bodyToMono(Person.class); final UUID id = UUID.randomUUID(); return created(UriComponentsBuilder.fromPath("people/" + id).build().toUri()) .contentType(APPLICATION_JSON) .body( fromPublisher( person.map(p -> new Person(p, id)).flatMap(personManager::save), Person.class)); }  即使只是从第一行开始,我们就可以看到,这种get方法的工作方式已经不同了。由于这是一个POST请求,它需要接受我们希望从请求主体持续存在的对象。由于我们试图插入单个记录,因此我们将使用请求的bodyToMono方法Person从正文中检索。如果您正在处理多个记录,则可能需要使用它们bodyToFlux。我们将CREATED使用created接受a 的方法返回状态URI以确定插入记录的路径。然后,get通过使用该fromPublisher方法将新记录添加到响应主体,然后采用与该方法类似的设置。形成该代码的代码Publisher稍有不同,但输出仍然Mono<Person>是一个重要的内容。为了进一步解释如何完成插入Person,从请求传入的内容将被映射到Person使用UUID我们生成的新内容,然后通过save调用传递给新内容flatMap。通过创建一个新的Person我们只将值插入我们允许的Cassandra中,在这种情况下,我们不希望UUID从请求体传入。所以说,这是关于处理程序。显然还有其他方法,我们没有经历。它们的工作方式都不相同,但都遵循相同的概念,ServerResponse如果需要,它返回一个包含适当状态代码和记录的体系。现在我们已经编写了所有我们需要的代码来获得基本的Spring WebFlux后端运行。剩下的就是将所有配置绑定在一起,这对Spring Boot来说很简单。@SpringBootApplication public class Application { public static void main(String args[]) { SpringApplication.run(Application.class); } }  我们应该研究如何真正使用代码,而不是结束这篇文章。Spring提供了WebClient该类来处理请求而不会阻塞。我们现在可以利用这个来测试应用程序,尽管WebTestClient我们也可以在这里使用它。该WebClient是你可以使用,而不是阻止什么RestTemplate产生反应的应用程序时。下面是一些调用在PersonHandler。中定义的处理程序的代码。public class Client { private WebClient client = WebClient.create("http://localhost:8080"); public void doStuff() { // POST final Person record = new Person(UUID.randomUUID(), "John", "Doe", "UK", 50); final Mono<ClientResponse> postResponse = client .post() .uri("/people") .body(Mono.just(record), Person.class) .accept(APPLICATION_JSON) .exchange(); postResponse .map(ClientResponse::statusCode) .subscribe(status -> System.out.println("POST: " + status.getReasonPhrase())); // GET client .get() .uri("/people/{id}", "a4f66fe5-7c1b-4bcf-89b4-93d8fcbc52a4") .accept(APPLICATION_JSON) .exchange() .flatMap(response -> response.bodyToMono(Person.class)) .subscribe(person -> System.out.println("GET: " + person)); // ALL client .get() .uri("/people") .accept(APPLICATION_JSON) .exchange() .flatMapMany(response -> response.bodyToFlux(Person.class)) .subscribe(person -> System.out.println("ALL: " + person)); // PUT final Person updated = new Person(UUID.randomUUID(), "Peter", "Parker", "US", 18); client .put() .uri("/people/{id}", "ec2212fc-669e-42ff-9c51-69782679c9fc") .body(Mono.just(updated), Person.class) .accept(APPLICATION_JSON) .exchange() .map(ClientResponse::statusCode) .subscribe(response -> System.out.println("PUT: " + response.getReasonPhrase())); // DELETE client .delete() .uri("/people/{id}", "ec2212fc-669e-42ff-9c51-69782679c9fc") .exchange() .map(ClientResponse::statusCode) .subscribe(status -> System.out.println("DELETE: " + status)); } } 不要忘了在Client某个地方实例化,下面是一个很好的偷懒方式来做到这一点!@SpringBootApplication public class Application { public static void main(String args[]) { SpringApplication.run(Application.class); Client client = new Client(); client.doStuff(); } } 首先我们创建一个WebClient。private final WebClient client = WebClient.create("http://localhost:8080"); 一旦创建,我们就可以开始做它的东西,因此doStuff方法。我们来分解一下POST发送给后端的请求。final Mono<ClientResponse> postResponse = client .post() .uri("/people") .body(Mono.just(record), Person.class) .accept(APPLICATION_JSON) .exchange(); postResponse .map(ClientResponse::statusCode) .subscribe(status -> System.out.println("POST: " + status.getReasonPhrase()));我写下这个稍有不同,所以你可以看到a Mono<ClientResponse>是从发送请求返回的。该exchange方法将HTTP请求发送到服务器。然后,只要响应到达,就会处理响应,如果有的话。使用WebClient我们指定我们想要POST使用post当然的方法发送请求。在URI随后与所添加的uri方法(重载的方法,这一个接受一个String但另一个接受URI)。我厌倦了说这个方法做了什么方法,所以,身体的内容随后与Accept头一起添加。最后我们通过电话发送请求exchange。请注意,媒体类型APPLICATION_JSON与POST路由器功能中定义的类型相匹配。如果我们要发送不同的类型,比如说TEXT_PLAIN我们会得到一个404错误,因为没有处理程序存在与请求期望返回的内容相匹配的地方。使用Mono<ClientResponse>通过调用返回exchange,我们可以绘制它的内容给我们所需的输出。在上面的例子中,状态代码被打印到控制台。如果我们回想一下post方法PersonHandler,请记住它只能返回“创建”状态,但如果发送的请求没有正确匹配,则会打印出“未找到”。我们来看看其他请求之一。client .get() .uri("/people/{id}", "a4f66fe5-7c1b-4bcf-89b4-93d8fcbc52a4") .accept(APPLICATION_JSON) .exchange() .flatMap(response -> response.bodyToMono(Person.class)) .subscribe(person -> System.out.println("GET: " + person));这是我们的典型GET要求。它看起来与POST我们刚刚经历的请求非常相似。主要区别在于uri,请求路径和UUID(作为String在这种情况下)作为参数来取代路径变量{id}并且主体留空。响应如何处理也是不同的。在这个例子中,它提取了响应的主体并将其映射到a Mono<Person>并打印出来。这可以在前面的POST例子中完成,但是响应的状态代码对于它的情况更有用。对于略有不同的观点,我们可以使用cURL发出请求并查看响应的样子。CURL -H "Accept:application/json" -i localhost:8080/people HTTP/1.1 200 OK transfer-encoding: chunked Content-Type: application/json [ { "id": "13c403a2-6770-4174-8b76-7ba7b75ef73d", "firstName": "John", "lastName": "Doe", "country": "UK", "age": 50 }, { "id": "fbd53e55-7313-4759-ad74-6fc1c5df0986", "firstName": "Peter", "lastName": "Parker", "country": "US", "age": 50 } ] 响应看起来像这样,显然它会根据您存储的数据而有所不同。请注意响应标题。transfer-encoding: chunked Content-Type: application/json    在transfer-encoding这里表示的是在可用于流式传输的数据块传输的数据。这就是我们需要的,因此客户可以对返回给它的数据采取反应态度。我认为这应该是一个停止的好地方。我们在这里已经涵盖了相当多的材料,希望能够帮助您更好地理解Spring WebFlux。还有一些其他的话题我想关注WebFlux,但是我会在单独的帖子中做这些,因为我认为这个主题足够长。    总之,在这篇文章中,我们非常简要地讨论了为什么你想在典型的Spring MVC后端中使用Spring WebFlux。然后我们看看如何设置路由和处理程序来处理传入的请求。处理程序实现了可以处理大多数REST动词的方法,并在响应中返回了正确的数据和状态代码。最后,我们研究了向后端发送请求的两种方式,一种是使用a WebClient直接在客户端处理输出,另一种使用cURL查看返回的JSON的外观。注意源码中使用的数据为:cassandra提示:项目源码下载:demo-springboot-webflux-0401.zip