首页
苏兮影视
随笔记
壁纸
更多
直播
时光轴
友联
关于
统计
Search
1
软件添加id功能按钮
760 阅读
2
v2ray节点搭建
731 阅读
3
typecho非常有特色的模块
549 阅读
4
QQxml消息卡片生成源码
503 阅读
5
网易云音乐歌单ID获取教程
476 阅读
谈天说地
建站源码
经验教程
资源分享
动漫美图
登录
Search
标签搜索
java
flutter
springboot
rust
安卓
linux
vue
docker
joe
快捷键
git
fish shell
maven
redis
netty
dart
groovy
js
设计模式
rpc
尽意
累计撰写
103
篇文章
累计收到
39
条评论
首页
栏目
谈天说地
建站源码
经验教程
资源分享
动漫美图
页面
苏兮影视
随笔记
壁纸
直播
时光轴
友联
关于
统计
搜索到
87
篇与
的结果
基于 Maven 多模块的 SpringCloud 微服务架构实战
随着微服务架构的普及,越来越多的项目开始采用分布式服务模式。在本文中,我们基于 SpringCloud Alibaba 构建一套简单但功能齐全的微服务架构。技术栈包括 Nacos、OpenFeign、SpringCloud Gateway、OkHttp 等,架构基于 Maven 的多模块项目管理方式。目标架构本项目采用多模块 Maven 项目管理,结构如下:springcloud-demo ├── pom.xml # 父 POM ├── gateway-service # 网关服务模块 ├── service-a # 服务 A 模块 ├── service-b # 服务 B 模块 └── nacos-server # Nacos 配置说明功能概览服务注册与发现:Nacos 作为服务注册中心,实现动态服务注册与发现。服务通信:使用 OpenFeign 实现跨服务的 HTTP 调用,支持负载均衡。统一网关:通过 SpringCloud Gateway 实现统一入口和路由管理。连接池优化:结合 OkHttp 提高 OpenFeign 的网络连接效率。配置管理:通过 Nacos 实现动态的集中配置管理。技术栈解析1. Nacos作用:作为服务注册与配置中心,支持动态服务发现和配置热更新。使用场景:服务注册发现、集中化配置管理。2. OpenFeign作用:基于声明式的 REST 客户端,简化服务间调用。优势:自动负载均衡,支持多种序列化方式和超时配置。3. SpringCloud Gateway作用:提供统一网关入口,支持路由转发、限流、熔断等功能。优势:对微服务访问统一管理,提升扩展性与安全性。4. OkHttp作用:为 OpenFeign 提供底层连接池支持,提高网络通信效率。优势:轻量高效,支持连接复用。父项目 POM 配置在父项目的 pom.xml 中,定义公共依赖和子模块:<modules> <module>gateway-service</module> <module>service-a</module> <module>service-b</module> </modules> <dependencyManagement> <dependencies> <!-- SpringCloud Alibaba 依赖管理 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.0.5.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Spring Boot 基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Nacos 服务发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- OpenFeign 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- SpringCloud Gateway 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies>各子模块实现1. Nacos 配置从 Nacos 官方 下载,启动命令:# 单机模式 sh startup.sh -m standaloneNacos 默认端口为 8848,访问 http://localhost:8848/nacos 查看管理界面,默认用户名和密码均为 nacos。2. Service A功能:提供简单的 REST 接口。依赖:Nacos 服务注册。application.ymlserver: port: 8081 spring: application: name: service-a cloud: nacos: discovery: server-addr: 127.0.0.1:8848Controller 示例@RestController @RequestMapping("/service-a") public class ServiceAController { @GetMapping("/hello") public String hello() { return "Hello from Service A"; } }3. Service B功能:调用 Service A 的接口。依赖:OpenFeign,Nacos 服务注册。application.ymlserver: port: 8082 spring: application: name: service-b cloud: nacos: discovery: server-addr: 127.0.0.1:8848Feign Client@FeignClient(name = "service-a") public interface ServiceAFeignClient { @GetMapping("/service-a/hello") String callServiceA(); }Controller 示例@RestController @RequestMapping("/service-b") public class ServiceBController { @Autowired private ServiceAFeignClient serviceAFeignClient; @GetMapping("/call") public String call() { return serviceAFeignClient.callServiceA(); } }4. Gateway 服务功能:通过网关统一路由到服务 A 和服务 B。依赖:SpringCloud Gateway,Nacos 服务注册。application.ymlserver: port: 8080 spring: application: name: gateway-service cloud: gateway: routes: - id: service-a-route uri: lb://service-a predicates: - Path=/service-a/** - id: service-b-route uri: lb://service-b predicates: - Path=/service-b/** nacos: discovery: server-addr: 127.0.0.1:8848配置 OkHttp 连接池为 OpenFeign 配置 OkHttp 优化性能: FeignConfig@Configuration public class FeignConfig { @Bean public OkHttpClient okHttpClient() { return new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build(); } @Bean public OkHttpFeignClient feignClient(OkHttpClient okHttpClient) { return new OkHttpFeignClient(okHttpClient); } }在 service-b 的 Feign Client 中即可使用配置的 OkHttp 连接池。测试流程启动 Nacos:确保 Nacos 服务运行在 8848 端口。启动各服务:按照顺序启动 gateway-service、service-a、service-b。访问接口:通过 Gateway 访问服务:http://localhost:8080/service-a/hello 返回 Hello from Service A。http://localhost:8080/service-b/call 通过 Feign 调用 Service A。
2024年12月04日
81 阅读
0 评论
2 点赞
RabbitMQ 消息队列详解及 Spring Boot 实战教程
2024年11月26日
110 阅读
0 评论
3 点赞
2024-11-26
RabbitMQ 是一个高效、可靠的消息代理中间件,广泛应用于分布式系统中,用于实现微服务之间的异步通信和解耦。一、RabbitMQ 基本概念1.1 核心概念队列(Queue):存储消息的容器,消息由生产者发送到队列,消费者从队列中取出进行处理。支持点对点(P2P)模式。交换机(Exchange):负责根据路由规则将消息分发到队列。常见交换机类型:Direct、Fanout、Topic。绑定(Binding):连接交换机和队列的规则,结合路由键(Routing Key)定义消息的路由方式。消息模型:点对点模式(P2P): 一条消息只能被一个消费者消费。发布订阅模式(Pub/Sub): 一条消息可以被多个消费者消费。二、三种交换机详解2.1 Direct 交换机路由方式: 消息根据路由键(Routing Key)精准匹配到指定队列。优点: 简单、高效,适用于明确路由需求的场景。缺点: 需要为每个队列绑定具体的路由键,维护成本较高。应用场景: 点对点通知,如订单支付成功通知。2.2 Fanout 交换机路由方式: 广播消息到所有绑定队列,无需路由键。优点: 适合广播场景,无需复杂路由配置。缺点: 不支持消息过滤,容易造成资源浪费。应用场景: 全网公告或系统事件通知。2.3 Topic 交换机路由方式: 根据通配符模糊匹配路由键。* 匹配一个单词,# 匹配零个或多个单词。优点: 灵活的路由规则,支持复杂消息分发需求。缺点: 配置复杂,性能略低于其他类型交换机。应用场景: 新闻分类推送或订阅系统。三、Spring Boot 与 RabbitMQ 整合3.1 项目依赖在 pom.xml 文件中添加 RabbitMQ 依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>3.2 RabbitMQ 配置两种方式方法一:使用 application.yml 配置在 application.yml 文件中配置 RabbitMQ:spring: rabbitmq: host: localhost port: 5672 username: guest password: guest方法二:通过配置类创建连接工厂import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQFactoryConfig { @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory factory = new CachingConnectionFactory(); factory.setHost("localhost"); factory.setPort(5672); factory.setUsername("guest"); factory.setPassword("guest"); return factory; } }3.3 交换机、队列及绑定的两种创建方式方法一:直接使用 new 创建import org.springframework.amqp.core.*; @Configuration public class RabbitMQDirectConfig { @Bean public Queue directQueue() { return new Queue("direct.queue"); } @Bean public DirectExchange directExchange() { return new DirectExchange("direct.exchange"); } @Bean public Binding bindingDirect(Queue directQueue, DirectExchange directExchange) { return BindingBuilder.bind(directQueue).to(directExchange).with("direct.key"); } }方法二:通过工厂类创建import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQFactoryConfig { @Bean public Queue fanoutQueue() { return QueueBuilder.durable("fanout.queue").build(); } @Bean public FanoutExchange fanoutExchange() { return ExchangeBuilder.fanoutExchange("fanout.exchange").durable(true).build(); } @Bean public Binding bindingFanout(Queue fanoutQueue, FanoutExchange fanoutExchange) { return BindingBuilder.bind(fanoutQueue).to(fanoutExchange); } }3.4 消息生产者与消费者消息生产者import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Component; @Component public class MessageProducer { private final RabbitTemplate rabbitTemplate; public MessageProducer(RabbitTemplate rabbitTemplate) { this.rabbitTemplate = rabbitTemplate; } public void sendDirectMessage(String message) { rabbitTemplate.convertAndSend("direct.exchange", "direct.key", message); } public void sendFanoutMessage(String message) { rabbitTemplate.convertAndSend("fanout.exchange", "", message); } public void sendTopicMessage(String message) { rabbitTemplate.convertAndSend("topic.exchange", "topic.test", message); } }消息消费者import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class MessageConsumer { @RabbitListener(queues = "direct.queue") public void receiveDirectMessage(String message) { System.out.println("Received Direct Message: " + message); } @RabbitListener(queues = "fanout.queue") public void receiveFanoutMessage(String message) { System.out.println("Received Fanout Message: " + message); } }3.5 单元测试使用 JUnit 测试消息发送功能:import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class RabbitMQTest { @Autowired private MessageProducer messageProducer; @Test public void testDirectMessage() { messageProducer.sendDirectMessage("Test Direct Exchange"); } @Test public void testFanoutMessage() { messageProducer.sendFanoutMessage("Test Fanout Exchange"); } }四、总结本文通过介绍 RabbitMQ 的核心概念,分析了三种交换机的优缺点及适用场景,并通过 Spring Boot 实现了 MQ 的完整配置与消息生产、消费功能。提供的两种实现方式(application.yml 配置和配置类方式,以及直接 new 和工厂创建方式)为不同项目需求提供了灵活选择。
Vue 3 和 WebRTC 实现点对点聊天应用
使用 Vue 3 和 WebRTC 来实现一个简单的点对点聊天应用,服务端使用 WebSocket (ws) 来建立信令服务器,用于连接和交换 WebRTC 所需的 SDP 和 ICE 候选信息。1. 项目初始化首先,我们创建一个基于 Vue 3 的项目:npm init vue@latest webrtc-chat cd webrtc-chat npm install然后,安装 WebSocket 依赖:npm install ws2. 创建 WebSocket 信令服务器我们使用 Node.js 和 ws 模块来创建信令服务器:// server.js const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { ws.on('message', (message) => { // 将消息广播给其他客户端 wss.clients.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); // 向新连接的客户端发送欢迎消息 ws.send(JSON.stringify({ type: 'welcome', message: 'Connected to WebSocket server' })); }); console.log('WebSocket server is running on ws://localhost:8080');运行服务器:node server.js3. 创建 Vue 3 应用在 src 文件夹中创建 components/Chat.vue 组件。<template> <div> <h2>点对点聊天</h2> <textarea v-model="message" placeholder="输入消息..."></textarea> <button @click="sendMessage">发送</button> <button @click="createOffer">发送 Offer</button> <div v-for="(msg, index) in messages" :key="index">{{ msg }}</div> </div> </template> <script> export default { data() { return { message: '', messages: [], peerConnection: null, dataChannel: null, ws: null }; }, mounted() { this.initializeWebSocket(); // 初始化 WebSocket this.initializePeerConnection(); // 初始化 PeerConnection }, methods: { // 初始化 WebSocket 连接 initializeWebSocket() { this.ws = new WebSocket('ws://localhost:8080'); // 接收来自信令服务器的消息 this.ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'offer') { this.handleOffer(data); // 处理接收到的 Offer } else if (data.type === 'answer') { this.handleAnswer(data); // 处理接收到的 Answer } else if (data.type === 'candidate') { this.addIceCandidate(data); // 添加接收到的 ICE 候选 } }; }, // 初始化 PeerConnection 和数据通道 initializePeerConnection() { this.peerConnection = new RTCPeerConnection(); // 监听 ICE 候选事件 this.peerConnection.onicecandidate = (event) => { if (event.candidate) { this.ws.send(JSON.stringify({ type: 'candidate', candidate: event.candidate })); // 将 ICE 候选发送到信令服务器 } }; // 创建数据通道 this.dataChannel = this.peerConnection.createDataChannel('chat'); this.dataChannel.onmessage = (event) => { this.messages.push(`Peer: ${event.data}`); // 接收消息并更新聊天记录 }; // 监听远程数据通道 this.peerConnection.ondatachannel = (event) => { this.dataChannel = event.channel; this.dataChannel.onmessage = (event) => { this.messages.push(`Peer: ${event.data}`); }; }; }, // 创建并发送 Offer async createOffer() { const offer = await this.peerConnection.createOffer(); await this.peerConnection.setLocalDescription(offer); this.ws.send(JSON.stringify({ type: 'offer', sdp: offer })); // 发送 Offer }, // 处理接收到的 Offer async handleOffer(data) { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp)); const answer = await this.peerConnection.createAnswer(); await this.peerConnection.setLocalDescription(answer); this.ws.send(JSON.stringify({ type: 'answer', sdp: answer })); // 发送 Answer }, // 处理接收到的 Answer async handleAnswer(data) { await this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp)); }, // 添加接收到的 ICE 候选 async addIceCandidate(data) { if (data.candidate) { await this.peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate)); } }, // 发送消息 sendMessage() { if (this.dataChannel && this.message) { this.dataChannel.send(this.message); // 通过数据通道发送消息 this.messages.push(`Me: ${this.message}`); // 更新本地聊天记录 this.message = ''; // 清空输入框 } } } }; </script> <style> textarea { width: 100%; height: 100px; margin-bottom: 10px; } button { display: block; margin-bottom: 10px; } </style>4. 启动应用运行以下命令以启动 Vue 应用:npm run dev打开浏览器开两个窗口访问 http://localhost:3000,点击发送offer即可进行实时的点对点聊天。
2024年11月15日
147 阅读
0 评论
3 点赞
Vue 3 中使用 Tailwind CSS
1. 项目初始化与安装 Tailwind CSS在 Vue 3 项目中使用 Tailwind CSS 需要先进行初始化配置。这里以 Vue CLI 或 Vite 为例,分别介绍两种常见的初始化方式。使用 Vue CLI创建 Vue 3 项目:vue create my-project cd my-project安装 Tailwind CSS:vue add tailwind使用 Vite创建 Vue 3 项目:npm create vite@latest my-project --template vue cd my-project安装 Tailwind CSS:npm install -D tailwindcss postcss autoprefixer npx tailwindcss init配置 tailwind.config.js 和 postcss.config.js(Vite 会自动生成这些文件,确保使用默认配置即可):tailwind.config.js:module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], theme: { extend: {}, }, plugins: [], }postcss.config.js:module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }2. 在 Vue 组件中使用 Tailwind CSSTailwind 的类名可以直接在 Vue 组件中使用,只需在模板中添加类名即可。比如,使用 Tailwind 设置按钮样式:<template> <button class="bg-blue-500 text-white p-2 rounded">Click Me</button> </template>你可以根据项目需求自定义类名,也可以使用 Tailwind 提供的响应式和伪类变体。例如,设置一个响应式的布局:<template> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="bg-gray-200 p-4">Item 1</div> <div class="bg-gray-200 p-4">Item 2</div> <div class="bg-gray-200 p-4">Item 3</div> </div> </template>3. 配置 Tailwind 的自定义主题Tailwind 提供了丰富的自定义配置选项,你可以在 tailwind.config.js 中修改颜色、字体、间距等。例如,修改默认的颜色主题:module.exports = { theme: { extend: { colors: { primary: '#FF6347', // 设置自定义颜色 }, }, }, }在 Vue 组件中使用自定义颜色:<template> <button class="bg-primary text-white p-2 rounded">Click Me</button> </template>4. 使用 Tailwind 的插件功能Tailwind 有很多官方插件,可以为你的项目带来额外的功能。例如,@tailwindcss/forms 插件可以改善表单元素的样式。安装插件:npm install @tailwindcss/forms在 tailwind.config.js 中引入插件:module.exports = { plugins: [ require('@tailwindcss/forms'), ], }使用表单插件后,你的表单元素将自动应用更好的样式:<template> <form> <input type="text" class="form-input mt-1 block w-full" /> <button class="bg-primary text-white p-2 rounded">Submit</button> </form> </template>5. 使用 Tailwind 的 JIT 模式(即时编译模式)Tailwind CSS 默认开启了 JIT 模式(在 2.x 版本后),这意味着只有在使用到的 CSS 类才会被编译到最终的 CSS 文件中,极大地减少了打包后的文件大小。若要显式开启 JIT 模式,可以在 tailwind.config.js 中进行设置:module.exports = { mode: 'jit', content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], theme: { extend: {}, }, }6. 优化 Tailwind 打包结果在生产环境中,使用 Tailwind CSS 可能会导致打包后的文件过大。为了减少文件大小,可以使用 purge 或 content 配置来确保只编译实际使用到的 CSS 类。在 tailwind.config.js 中启用内容扫描:module.exports = { content: [ "./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}" ], }这样,Tailwind 只会编译扫描到的文件中的 CSS 类,从而减少最终的 CSS 文件大小。7. 组合 Vue 3 的响应式特性与 TailwindTailwind 支持响应式设计,可以结合 Vue 3 的 v-bind 和计算属性等响应式特性来动态更新类名。通过 v-bind:class 可以动态绑定类。<template> <div :class="buttonClass"> <button>Click Me</button> </div> </template> <script setup> import { ref } from 'vue' const isPrimary = ref(true) const buttonClass = computed(() => ({ 'bg-blue-500 text-white': isPrimary.value, 'bg-gray-500 text-black': !isPrimary.value, })) </script>8. 常见问题解决CSS 样式不生效:如果发现 Tailwind 样式没有生效,检查是否正确配置了 tailwind.config.js,并确保 content 配置中包含了你的组件路径。文件过大:开启 JIT 模式和内容扫描,以确保只编译用到的类。9. 资源与参考Tailwind CSS 官方文档Vue 3 官方文档Tailwind CSS 与 Vue 3 集成教程
2024年11月15日
107 阅读
0 评论
3 点赞
v2ray节点搭建
快速开始:https://233boy.com/v2ray/v2ray-script/详细说明:https://bulianglin.com/archives/guide.html
2024年11月06日
731 阅读
0 评论
2 点赞
1
...
3
4
5
...
18