• [技术干货] Java智慧校园系统源码springboot + vue智慧学校源码 微信小程序+电子班牌
    Java智慧校园系统源码springboot + vue智慧学校源码 微信小程序+电子班牌智慧校园的建设逐渐被师生、家长认可接受,智慧校园通过对在校师生、教务等所有人员的信息以及各种信息搜集与储存,进行数据优化与管理,为师生们提供更加智能化的校园服务。未来智慧校园将不再是一个陌生词,而会真正地应用在更多的校园管理中,让我们的校园生活变得更加美好智慧校园技术架构:后端:Java  框架:springboot 前端页面:vue 小程序:小程序原生开发智慧校园移动家长端应用:通知管理、图片管理、班级考勤、综合素质评价、视频管理、请假管理、成绩管理、个人信息、进离校管理、教师通讯录、家长留言、课堂点名、家长会签到、活动报名、放学管理、学生评价。智慧校园移动移动教师端应用:设备管理、通知管理、图片管理、班级考勤、综合素质评价、视频管理、请假管理、成绩管理、个人信息、进离校管理、家长通讯录、教师通讯录、教师课表、AI智能分析、课堂点名、课堂授课、家长会签到、活动报名、积分商城、倒计时、班级德育、体温检测、放学管理、学生评价。▷学校信息:学校信息自定义格式展示,可上传学校相册,自定义学校基础信息栏。支持管理员或教师对学校的基本学校信息进行编辑并浏览,通过编辑提交后全校可查看▷学科设置:学校学科展示,添加学科,双击修改学科、删除学科。▷组织架构:支持管理员单个创建以及批量创建部门,部门层级最少不低于四级架构;组织架构支持可视展示。▷教师数据:支持教师多部门多角色,支持粘贴复制简易化批量导入,批量导入支持字段错误检索,支持一键重置密码。▷学生数据:支持单个学生信息录入、修改、删除,支持粘贴复制简易化批量导入,支持一键重置密码▷教室管理:班级绑定教室,教室绑定设备,通过教室切换班级即可实现设备一键切换班级,支持添加、删除、修改教室信息。▷权限管理:数据权限、功能权限、角色管理、应用权限。▷课表管理:支持支持管理员课表模板设置,支持复制粘贴简易化批量导入,批量导入支持学科及任课教师错误检索。智慧校园系统框架技术参数要求(提供不少于3张的证明截图,并附上原厂公章,原件备查):1、系统后台采用Java开发语言,前端采用VUE开发框架2、★系统采用微服务springcloud架构的作为后台服务器架构3、★系统支持搭建开发具备服务注册发现、客户负载均衡、服务间通信的微服务架构4、系统支持使用SpringCloud Eurek、SpringCloud Ribbon、restTemplate 等组件进行开发5、注册中心:接受服务提供者的注册,提供服务注册者的存储信息(如:IP、端口号、服务名)与微服务保持心跳6、服务提供者:注册自己的服务到服务中心,服务提供者向注册中心发送自己的信息以及一些健康状态。7、服务消费者:定期向注册中心发送查询请求,以定期获得服务提供者的一些信息(如:IP,端口号,服务名)。8、使用Quartz框架实现任务调度(如:对不同的学校在不同的时间段执行上课提醒)9、使用Mina网络应用型框架(实时接收第三方进离校情况,闸机,人脸机等),后续可发展实时通信组件有:feing(实现服务对服务之间的调用)zuul(网关负载均衡,反向代理,隐藏真实ip地址)
  • [技术干货] spring boot3登录开发-2(1图形验证码接口实现) -转载
     前置条件 本文衔接上文,请从上文开始  spring boot3x登录开发-上(整合jwt)-CSDN博客 https://blog.csdn.net/qq_62262918/article/details/135964626?spm=1001.2014.3001.5502  内容简介 上文我们已经整合好了jwt,本文我们开始实现图形验证码接口的实现。  通过糊涂工具包的图形验证码工具完成获取验证码接口 通过redis缓存key(验证码id)-value(验证码内容) 图形验证码接口实现 导入糊涂工具依赖 pom.xml:  <dependency>     <groupId>cn.hutool</groupId>     <artifactId>hutool-all</artifactId>     <version>5.8.25</version> </dependency> 接口分析 前端的登录表单有个验证码id字段,第一次打开登录页面默认会请求验证码接口,那么后端验证码接口将返回验证码图片的base64编码和验证码id,前端需要将验证码id保存到表单对象的验证码id字段,同时把验证码图片显示。用户填写账密、验证码点击登录,表单对象将携带账密和验证码id和用户键入的验证码内容提交到后端,后端需要根据此验证码id去查redis跟用户提交的比对。  分析完我们就可以知道怎样设计这个接口了。  接口接收一个验证码id参数,判断这个参数如果是null则生成一个验证码id,不为null则直接拿它去生成redis缓存验证码内容的key,接着将验证码图片同id返回给前端。   首先定义验证码接口数据对象  import lombok.Builder; import lombok.Data;   /**  * @author mijiupro  */ @Data @Builder public class CaptchaVO {     //验证码id     private  String captchaId;     //验证码图片base64编码     private  String captchaImage; } 编写验证码接口 这里用到了redis,需要整合好:  Spring Boot3整合Redis-CSDN博客 https://blog.csdn.net/qq_62262918/article/details/136067550?spm=1001.2014.3001.5501  import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.CircleCaptcha; import com.mijiu.commom.model.vo.CaptchaVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;   import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit;   /**  * @author mijiupro  */ @RestController @RequestMapping("/Captcha") @Tag(name = "验证码接口", description = "验证码接口相关操作") public class CaptchaController {     private final StringRedisTemplate stringRedisTemplate;       public CaptchaController(StringRedisTemplate stringRedisTemplate) {         this.stringRedisTemplate = stringRedisTemplate;     }          @GetMapping("/graph-captcha")     @Operation(summary = "获取验证码")     public CaptchaVO getCaptcha(String captchaId) {         // 创建一个图像验证码宽度为130,高度为48,包含4个字符,干扰线10个         CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(130, 48, 4, 10);         // 获取验证码的文本         String captchaText = circleCaptcha.getCode();         // 获取验证码图片的Base64编码         String captchaImageBase64Data = circleCaptcha.getImageBase64Data();         // 如果没有传入captchaId,则生成一个随机字符串作为captchaId         captchaId = Optional.ofNullable(captchaId).orElseGet(() -> UUID.randomUUID().toString());         // 保存验证码文本到Redis中,有效期30秒         stringRedisTemplate.opsForValue().set("captcha:" + captchaId, captchaText, 30, TimeUnit.SECONDS);           return CaptchaVO.builder()                 .captchaId(captchaId)                 .captchaImage(captchaImageBase64Data)                 .build();     }   }  测试验证码接口 这里使用Knife4jConfig(swigger3)测试,也可以用浏览器地址栏、Postman等测试  Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客 https://blog.csdn.net/qq_62262918/article/details/135761392?spm=1001.2014.3001.5502                            原文链接:https://blog.csdn.net/qq_62262918/article/details/136064820 
  • [技术干货] 【监控】spring actuator源码速读-转载
     1.前言 版本:spring-boot-starter-actuator  2.6.3  阅读源码一定要带着疑问去阅读,这个疑问就是你阅读的主线,不然在浩如烟海的源码里面很容易迷路。我们当前的疑问是什么?之前我们已经聊过spring actuator的使用了:  Spring Boot 监控_springboot 监控-CSDN博客  本文要搞清楚的两个问题在于:  EndPoint是怎么被注入IOC又怎么暴露出去能通过HTTP访问到的?  EndPoint是怎么实现监控能力的?  2.先搂一眼EndPoint 首先我们找一个EndPoint来看看,此处以HealthEndPoint为例。点看源码我们可以看到这个EndPoint被@EndPoint注解所注释,id为health。然后其中的2个方法被@ReadOperation所注释:   这里其实猜都能猜到被@EndPoint注解,然后被注解的类被归类为EndPoint,然后被集中暴露出去,变成可访问的。  3.EndPoint如何被注入 我们是通过stater来引入actuator的,Spring Boot体系内如何注入stater的?那肯定是通过autoConfiguration来的撒。点进actuator的配置文件也可以看到:   于是我们来到spring-boot-starter-actuator来看看,看看它的spring.factories里面注入了些什么:  见名知意了,这些众多的XXXautoConfiguration是拿来做什么的就不必多说了吧,health、metrics......分门别类,各种EndPoint的autoConfiguration。   我们来看看HealthEndpointAutoConfiguration里面做了什么:  其实就是加载了HealthEndpointConfiguration、ReactiveHealthEndpointConfiguration、HealthEndpointWebExtensionConfiguration、HealthEndpointReactiveWebExtensionConfiguration这几个类  @Configuration(     proxyBeanMethods = false ) @ConditionalOnAvailableEndpoint(     endpoint = HealthEndpoint.class ) @EnableConfigurationProperties({HealthEndpointProperties.class}) @Import({HealthEndpointConfiguration.class, ReactiveHealthEndpointConfiguration.class, HealthEndpointWebExtensionConfiguration.class, HealthEndpointReactiveWebExtensionConfiguration.class}) public class HealthEndpointAutoConfiguration {     public HealthEndpointAutoConfiguration() {     } } 我们先看HealthEndpointConfiguration,它里面向IOC中注入了health的Registry,HealthContributorRegistry中注册了HealthContributor类型的实体。   我们随便打开一个health的EndPoint,发现它都继承同一个父类:   而这个父类实现了HealthContributor接口:   所以其实就是在将注册器注入IOC的时候,就将所有属于该类型的EndPoint注册到注册器中了。  4.EndPoint如何被暴露 4.1.如何通过http暴露 在SpringBoot体系中,谁来负责http请求?那当然是SpringMVC的DispatcherServlet。把path和对应处理的类注册到DispatcherServlet中就行了。spring actuator就是这样对外通过HTTP的方式暴露EndPoint的。  回到spring.factories,找ManagementContextAutoConfiguration,这个类中完成了通过HTTP的方式来暴露EndPoint的过程:   这个类的代码并不多,我们去掉不要的部分,把和对外暴露EndPoint相关的代码保留,来读一下:  @ManagementContextConfiguration(     proxyBeanMethods = false ) @ConditionalOnWebApplication(     type = Type.SERVLET )//只能在Web环境中生效 public class ServletEndpointManagementContextConfiguration {     public ServletEndpointManagementContextConfiguration() {     } ​     @Configuration(         proxyBeanMethods = false     )     @ConditionalOnClass({DispatcherServlet.class})//当SpringMVC存在时向IOC中注入     public static class WebMvcServletEndpointManagementContextConfiguration {         public WebMvcServletEndpointManagementContextConfiguration() {         }         @Bean         public ServletEndpointRegistrar servletEndpointRegistrar(WebEndpointProperties properties, ServletEndpointsSupplier servletEndpointsSupplier, DispatcherServletPath dispatcherServletPath) {             return new ServletEndpointRegistrar(dispatcherServletPath.getRelativePath(properties.getBasePath()), servletEndpointsSupplier.getEndpoints());//核心的一步,将EndPoint和对于的Path注册给DispatcherServlet         }     } }  最后就是开头我们看见的在HealthEndPoint被@ReadOperation注解的方法,就相当于@RequetMapping,拿来处理读请求的。  4.2.如何通过jmx暴露 jmx的对外暴露更简单。直接找JmxEndpointAutoConfiguration:   进去整个逻辑一目了然,去扫Jmx的EndPoint然后注册进mBeanServer里:   5.EndPoint是怎么实现监控能力的 spring actuator获取各种监控的值是怎么获取到的?  内置指标获取: Spring Boot 提供了一些内置的监控指标获取器,用于获取常见的监控数据,比如 JVM 内存使用情况、系统负载、数据库连接池状态等。这些指标获取器会周期性地获取数据,并将其暴露为 Actuator 端点,以便外部系统或者工具可以通过相应的接口来获取。例如,MemoryHealthIndicator 获取 JVM 内存使用情况,DataSourceHealthIndicator 获取数据库连接池状态等。  自定义指标获取: 除了内置的指标获取器外,开发者还可以通过实现 HealthIndicator 接口来自定义监控指标获取器,用于获取应用程序特定的监控数据。HealthIndicator 接口定义了一个 health() 方法,用于返回健康状态信息。开发者可以在 health() 方法中编写自定义的监控逻辑,比如检查某个依赖服务的可用性、计算某个指标的值等。  JMX 获取: Spring Actuator 还可以通过 Java Management Extensions (JMX) API 来获取一些系统级的监控数据,比如 JVM 运行时信息、操作系统信息等。Spring Actuator 中的一些监控指标获取器会使用 JMX API 来获取数据,然后将其暴露为 Actuator 端点。例如,JvmInfoContributor 使用 JMX API 来获取 JVM 运行时信息。  系统调用获取: 有些监控数据可能需要通过系统调用来获取,比如获取操作系统的 CPU 使用率、磁盘使用情况等。Spring Actuator 中的一些监控指标获取器会使用系统调用来获取这些数据,然后将其暴露为 Actuator 端点。  6.知道这些的意义是什么 本文是作者java监控系列文章的第三篇,之前两篇文章我们着重讲了java监控的基石——JMX。  【JMX】JAVA监控的基石-CSDN博客  详解tomcat中的jmx监控-CSDN博客  在spring actuator里面我们知道了目前市面上一个成熟的框架是如何通过http、JMX等不同方式来对外暴露监控能力的。基本上走到这里我们就已经对JAVA整个的监控技术体系最核心的部分有了认识了。作为监控框架来说核心点有哪些?无非是:  获取数据  对外暴露口子  监控的核心肯定是怎么获取数据以及如何将获取的数据暴露出去,只要这两点搞定了,后面的对接各种可视化平台就很好办了。所有知道为啥这篇文章为啥要关心spring actuator这些地方了吧,主要是看看实现思想。 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Joker_ZJN/article/details/136154345 
  • [技术干货] Spring Cloud与Spring的区别
    前言随着微服务架构的兴起,Spring Cloud逐渐崭露头角,成为了构建分布式系统的重要工具。对于很多初次接触Spring Cloud的开发者来说,可能不太清楚它与传统的Spring框架有何区别。本文旨在探讨Spring Cloud与Spring之间的主要差异。一、Spring框架简介Spring是一个开源的Java平台,它提供了一套全面的编程和配置模型,用于构建企业级应用程序。Spring的核心特性包括依赖注入(DI)、面向切面编程(AOP)、数据访问抽象等。通过Spring,开发者可以更加高效、简洁地开发应用程序。二、Spring Cloud简介Spring Cloud是基于Spring Boot的一套微服务工具集,它提供了一整套微服务解决方案,包括服务发现、配置管理、熔断器、负载均衡等。Spring Cloud的目标是让微服务架构的搭建变得更加简单、快速和可靠。三、Spring Cloud与Spring的主要区别关注点不同:Spring框架主要关注的是应用程序的本身,提供了一系列的基础功能,如依赖注入、事务管理等。而Spring Cloud则更加关注微服务架构中的各个组件和服务之间的通信与协作,为构建分布式系统提供了丰富的工具和解决方案。组件和服务的集成:Spring Cloud集成了许多优秀的开源项目,如Netflix的Eureka、Hystrix、Zuul等,这些组件共同构成了微服务架构的核心部分。而Spring本身并不包含这些组件,需要开发者自行集成。服务治理:Spring Cloud提供了强大的服务治理功能,包括服务发现、配置管理、熔断器、负载均衡等。这些功能使得微服务架构更加健壮、可靠和易于维护。相比之下,Spring本身并不提供这些服务治理功能。部署和扩展性:Spring Cloud的设计初衷就是为了支持快速部署和横向扩展,它允许开发者将应用程序拆分成多个独立的服务,每个服务都可以独立部署和扩展。而传统的Spring应用程序通常需要整体部署,扩展性相对较差。与Spring Boot的整合:Spring Cloud是建立在Spring Boot基础之上的,因此它充分利用了Spring Boot的优点,如自动配置、快速启动等。这使得开发者在构建微服务应用程序时,可以更加高效、简洁地开发、部署和维护。四、总结综上所述,Spring Cloud与Spring的主要区别在于它们的关注点、组件和服务的集成、服务治理、部署和扩展性以及与Spring Boot的整合。Spring Cloud作为一套微服务工具集,为构建分布式系统提供了丰富的解决方案,使得开发者可以更加轻松地应对复杂的业务需求。然而,在使用Spring Cloud时,也需要关注其复杂性、学习曲线和潜在的兼容性问题。因此,在选择使用Spring Cloud还是传统的Spring框架时,需要根据项目的具体需求和团队的实际情况进行权衡。
  • [技术干货] Spring Cloud定位问题思路
    一般来说,问题都不是Spring Cloud 本身的Bug导致的。因此,读者排查问题的思路不妨按照以下步骤展开。 1. 排查配置问题 首先排查配置有无问题,举几个简单的例子。·YAML缩进是否正确 曾经有朋友发现Spring Cloud应用程序无法正常启动,或配置无法正常加载。经笔者协助,发现仅仅是YAML配置文件缩进不正确。类似问题应在编码的过程中严格规避。 配置属性是否正确 配置的属性编写错误,也是一个非常常见的问题。尽管该问题很低级,但从笔者的观察来看,不少初学者都会遇到这类问题。 在很多场景下,这类问题可借助IDE的提示功能来排查--当IDE给出警告或没有自动提示时,应格外注意。·配置属性的位置是否正确 配置属性位置不正确可能会导致应用的不正常。举几个常见的例子。 -应当配置在EurekaClient项目上的属性,配置在了Eureka Server项目上。 -应当写在bootstrap.yml 中的属性,写在了application.yml中,例如:spring: cloud: config: uri: http://localhost:8080/该属性应当存放在bootstrap.yml 中。 -应当写在application.yml的属性,写在了bootstrap.yml中,例如: cureka.client.healthcheck.enabled=true 2排查环境问题 若确认配置无误,即可考虑运行环境是否存在问题。举几个例子:.环境变量 例如Java 环境变量、Maven环境变量以及Docker容器环境变量等。当应用无法正常工作时,应该确保环境变量配置正确。.依赖下载是否完整 曾经有朋友遇到应用无法正常启动的问题,最终发现仅仅是依赖没有下载完整所致。因此,建议在启动应用前,使用以下命令打包,从而确认依赖的完整性。 mvn clean package.网络问题 微服务之间通过网络保持通信,因此,网络常常是排查问题的关键。当问题发生时,可优先排查网络问题。 3.排查代码问题 若经过以上步骤,依然没有定位到Spring Cloud的问题,那么可能是编写的代码出了问题。很多时候,常常因为少了某个注解,或是依赖缺失,而导致了各种异常。在许多场景下,设置合理的日志级别,会对问题的定位有奇效。 4.排查 Spring Cloud自身的问题 若确定不是自身代码问题,就可Debug一下Spring Cloud的代码了。同时,可在 GitHub等平台给Spring Cloud项目组提交Issue,然后参考官方回复,尝试规避相应问题。若问题无法规避,就需要对Spring Cloud进行扩展,或者修复Spring Cloud 的 Bug,从而满足需求。此时,请不要忘记在Spring Cloud的GitHub上Pull Request,协助官方改进Spring Cloud,让Spring Cloud更加完善、稳定。
  • 使用Spring Cloud Bus自动刷新配置
    使用/refresh端点手动刷新配置,但如果所有微服务节点的配置都需要手动去刷新,工作量可想而知。不仅如此、随着系统的不断扩张,会越来越难以维护。因此、实现配置的自动刷新是很有必要的、本节将使用SpringCloud Bus实现配置的自动刷新。 Spring Cloud Bus使用轻量级的消息代理(例如RabbitMQ、Kafka等)连接分布式系统的节点,这样就可以广播传播状态的更改(例如配置的更新)或者其他的管理指令。可将 Spring Cloud Bus想象成一个分布式的Spring Boot Actuator。微服务A的所有实例都通过消息总线连接到了一起,每个实例都会订阅配置更新事件。当其中一个微服务节点的/bus/refresh端点被请求时,该实例就会向消息总线发送一个配置更新事件,其他实例获得该事件后也会更新配置。接下来为项目整合SpringCloud Bus并实现自动刷新。 1.复制项目microservice-config-client-refresh,将ArtifactId修改为microservice-config -client-refresh-cloud-bus。 2.为项目添加spring-cloud-starter-bus-amqp的依赖。 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId></dependency> 3.在bootstrap.yml中添加以下内容: spring:    rabbitmq:    9.9     port: 5672    host: localhost         username: guest        password: guest这样代码就改造完成了。测试:1.启动 microservice-config-server 如下的内容。    2启动 microservice-config-client-refresh-cloud-bus,可发现此时控制台会输出类似     [ main] o.s.b.a.e.mvc.EndpointHandlerMapping    :Mapped"{[/bus/     refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh( java.lang.String)由结果可知,此时项目有一个/bus/refresh端点。 3.将 microservice-config-client-refresh-cloud-bus的端口改为8082,再启动一个节点。 4.访问http://localhost:8081/profile,可获得结果:dev-1.0。 5.将Git 仓库中的 microservice-foo-dev.properties 文件内容修改为如下内容。 profile=dev-1.0-bus 6.发送 POST 请求到其中一个Config Client实例的/bus/refresh端点,例如: curl-X POST http://localhost:8081/bus/refresh 7.访问两个ConfgClient节点的/profle端点,发现两个节点都会返回dev-1.8-bus,说明配置内容已被刷新。 借助Git 仓库的 WebHooks,即可轻松实现配置的自动刷新,
  • [技术干货] spring cloud config统一管理微服务配置
    为什么要统一管理微服务配置 对于传统的单体应用,常使用配置文件管理所有配置。例如一个Spring Boot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}。当然也可借助Maven的 Profile实现环境切换。 然而,在微服务架构中,微服务的配置管理一般有以下需求: ·集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。 .不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。 ·运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。.配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。 综上所述,对于微服务架构而言、一个通用的配置管理机制是必不可少的,常见做法是使用配置服务器管理配置。 Spring Cloud Config为分布式系统外部化配置提供了服务器端和客户端的支持,它包括Config Server和Config Client两部分。由于Config Server和Config Client 都实现了对 Spring Environment和PropertySource抽象的映射,因此,SpringCloud Config非常适合 Spring应用程序,当然也可与任何其他语言编写的应用程序配合使用。 Config Server是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置内容(也可使用Subversion、本地文件系统或Vault存储配置,限于篇幅,本书不作讨论),因此可以很方便地实现对配置的版本控制与内容审计。 Config Client是Config Server的客户端,用于操作存储在Config Server中的配置属性。各个微服务在启动时,会请求Config Server 以获取所需要的配置属性,然后缓存这些属性以提高性能。 
  • [技术干货] 【SpringCloud】这一次终于使用MQ解决了Eureka服务下线延迟感知问题-转载
     前言 其实,“通过Redis手动更新Ribbon缓存来解决Eureka微服务架构中服务下线感知的问题”是一种解,但不是最优解  1.痛点 上一篇文章的标题是: 通过Redis手动更新Ribbon缓存来解决Eureka微服务架构中服务下线感知的问题 当时在文章的末尾就指出,使用Redis+AOP的方式有很多漏洞,只有在服务调用方发送调用请求的情况下才会触发切面中更新Ribbon缓存的逻辑。如果每次在发布Eureka新服务的场景下,告警的接口都能准确定位到,那将这些接口方法通过切面去针对性的加上更新Ribbon缓存的前置操作完全是没问题的。但是如果告警接口数量众多,并且无法定位,上述方法就有些不够看了。  2.解决方案 于是,基于此种困境,我想到了用mq的事件驱动模式来推进Ribbon缓存更新(“下线”这一事件驱动,而不是“发送跨服务调用请求”这一事件),具体如下:  即,当服务被调用方中调用了下线接口下线了指定服务,会生产消息到MQ里,服务被调用方会监听这个队列去消费消息,并通过消费消息这一事件(消费下线服务端口信息)去驱动更新Ribbon缓存。 说明: 在以前我觉得用MQ不能做下线,压测了很多次也没成功,这本质还是没搞懂Eureka-Server,Eureka-Client,Ribbon三者的关系和之间的动作,其实这个体系里有两个非常关键的点(在配置文件中设置),可以直接影响无感知下线的结果,需要动态调整:那就是要关闭Eureka-server的三级缓存useReadOnlyResponseCache: false,并且缩短Eureka-Client端向Eureka-server端拉取服务列表的时间registry-fetch-interval-seconds: 3。可能这里大家看到去改配置有点鸡肋并且在实际场景中也不太现实,但别急,暂时先往下看,后面我会专门写一篇文章来解决这一问题  3.具体实现 3.1配置RabbitMQ 1.配置RabbitMQ(安装这些大家可以去看看平台比较成熟的文章)这里就不写了,我是直接在服务器上用docker容器化运行的:  在调用方与被调用方都配好MQ  3.2生产下线消息 首先声明一个队列:  @Configuration @EnableRabbit public class RabbitMqConfig {     @Bean     public Queue theQueue() {         return new Queue("SERVER_LIST");     } } 服务下线接口处,生产下线消息到MQ,向这接口/service-down-list发送GET请求,传递指定的下线服务实例信息即可下线服务,即http://localhost:8081/control/service-down-list?portParams=8083就下线了8083服务实例      @Value("${eureka-server.ipAddress}")     private String ipAddress;     @Value("${eureka-server.appName}")     private String appName;     @Value("${DIY_QUEUE.VALUE}")     private String queueName;     @GetMapping(value = "/service-down-list")     public String offLine(@RequestParam List<Integer> portParams) {         List<Integer> successList = new ArrayList<>();         //得到服务信息         List<InstanceInfo> instances = eurekaClient.getInstancesByVipAddress(appName, false);         List<Integer> servicePorts = instances.stream().map(InstanceInfo::getPort).collect(Collectors.toList());          //去服务列表里挨个下线         OkHttpClient client = new OkHttpClient();         log.error("开始时间:{}", System.currentTimeMillis());         portParams.parallelStream().forEach(temp -> {             if (servicePorts.contains(temp)) {                 String url = "http://" + ipAddress + ":" + temp + "/control/service-down";                 try {                     Response response = client.newCall(new Request.Builder().url(url).build()).execute();                     if (response.code() == 200) {                         log.debug(temp + "服务下线成功");                         successList.add(temp);                     } else {                         log.debug(temp + "服务下线失败");                     }                 } catch (IOException e) {                     log.error(e.toString());                 }             }         });         //todo MQ通知         HashMap<String, List<Integer>> portInfo = new HashMap<>();         portInfo.put(appName,successList);         rabbitTemplate.convertAndSend(queueName,portInfo);         return successList + "优雅下线成功";     } 这里向MQ的队列里传递了下线的服务实例端口信息  3.3更新Ribbon缓存 服务调用方通过“下线“这一事件驱动Ribbon缓存更新  /**  * 消费者  */ @Slf4j @Component public class Consumer {      @Resource     SpringClientFactory springClientFactory;     @Resource     ClearRibbonCacheBean clearRibbonCacheBean;      @RabbitListener(queues = "SERVER_LIST")     public void listenWorkQueue1(HashMap<String, List<Integer>> message) {         log.debug("消费者1接收到消息——" + message + "时间为:" + LocalTime.now());         for (String key : message.keySet()) {             List<Integer> value = message.get(key);             log.debug("Key: " + key);             log.debug("Value: " + value);             if (ObjectUtils.isNotEmpty(value)) {                 clearRibbonCacheBean.clearRibbonCache(springClientFactory, value.toString(), key);             }             log.debug("现在的所有服务列表:{}", springClientFactory.getLoadBalancer(key).getAllServers());         }     } } 清理Ribbon缓存的Bean:  /**  * 手动清除Ribbon缓存  */ @Configuration @Slf4j public class ClearRibbonCacheBean {     /**      * 削减      */     public static boolean cutDown(List<Integer> ports, Server index) {         return ports.contains(index.getPort());     }      public void clearRibbonCache(SpringClientFactory clientFactory, String portParams,String appName) {         // 获取指定服务的负载均衡器         ILoadBalancer loadBalancer = clientFactory.getLoadBalancer(appName);         //在主动拉取可用列表,而不是走拦截器被动的方式——这里为什么获取可用的之后还要过滤,就是因为所谓的可用不是实时的可用而是缓存中的可用         List<Server> reachableServers = loadBalancer.getReachableServers();//这里从客户端获取,会等待客户端同步三级缓存         //过滤掉已经下线的端口,符合条件端口的服务过滤出来         List<Integer> portList = StringChange.stringToList(portParams);         List<Server> ableServers = reachableServers.stream().filter(temp -> !cutDown(portList, temp)).collect(Collectors.toList());         log.debug("可用服务列表:{}", ableServers);         // 在某个时机需要清除Ribbon缓存         ((BaseLoadBalancer) loadBalancer).setServersList(ableServers); // 清除Ribbon负载均衡器的缓存     } 3.4压测 运行项目,调用下线接口并压测来模拟一下线上场景: 此时我们调用下线接口,下线8083服务实例:  压测结果,均无异常: 观察服务实例的日志输出: 未下线的8081,8084  下线的8083  这说明,Eureka服务下线感知的延迟已经完全被消除  4.优化 以上的MQ还是采用简单队列的模式,即生产者生产一条消息到队列中,该消息也只能被一个消费者消费到。在微服务架构中,用户微服务肯定不只是被单方面调用,而是会被多方调用。那这就要求我们不能单纯只将消息生产到队列里,应该通过广播的模式进行消息的分发。为了更方便交换机与队列的灵活绑定,以及方便扩展,采用Topic话题的模型进行消息的广播:  声明一个新队列:      @Bean     public Queue theQueue() {         return new Queue("USER-QUEUE");     } 将生产消息的地方改为携带一个routingkey并发送到交换机中:    //todo MQ通知   HashMap<String, List<Integer>> portInfo = new HashMap<>();   portInfo.put(appName,successList);   rabbitTemplate.convertAndSend(exchangeName,"USER.SERVICE-DOWN",portInfo);// 这个队列以后可能会发USER话题下的很多信息 1 2 3 4 将消费者端的消息监听器进行改造,变为监听指定话题的消息:   @RabbitListener(bindings = @QueueBinding(             value = @Queue(name = "USER-QUEUE"),             exchange = @Exchange(name = "USER-TOPIC", type = ExchangeTypes.TOPIC),             key = "USER.SERVICE-DOWN")     )     public void listenWorkQueue1(HashMap<String, List<Integer>> message) {         log.debug("消费者1接收到消息——" + message + "时间为:" + LocalTime.now());         for (String key : message.keySet()) {             List<Integer> value = message.get(key);             log.debug("Key: " + key);             log.debug("Value: " + value);             if (ObjectUtils.isNotEmpty(value)) {                 clearRibbonCacheBean.clearRibbonCache(springClientFactory, value.toString(), key);             }             log.debug("现在的所有服务列表:{}", springClientFactory.getLoadBalancer(key).getAllServers());         }     }  ————————————————                              @CSDN-懒羊羊.java的原创                          原文链接:https://blog.csdn.net/weixin_57535055/article/details/135560050 
  • [技术干货] 【SpringCloud Nacos】 微服务治理介绍及Nacos引入初体验 -转载
     前言 在开始今天的学习之前,大家先来思考一个问题:  通过上一章的操作,我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:  一旦服务提供者地址变化,就需要手工修改代码; 一旦是多个服务提供者,无法实现负载均衡功能; 一旦服务变得越来越多,人工维护调用关系困难; 那么应该怎么解决呢,这时候就需要通过注册中心动态的实现服务治理。  服务治理介绍 什么是服务治理 服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。  服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。 服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实例的访问。  通过上面的调用图你会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构非常重要的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:  1、服务发现 服务注册:保存服务提供者和服务调用者的信息; 服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息; 2、服务配置 配置订阅:服务提供者和服务调用者订阅微服务相关的配置; 配置下发:主动将配置推送给服务提供者和服务调用者; 3、服务健康检测 检测服务提供者的健康情况,如果发现异常,执行服务剔除。 常见的注册中心 Zookeeper Zookeeper 是一个分布式服务框架,是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。  Eureka Eureka 是 SpringCloud Netflix 中的重要组件,主要作用就是做服务注册和发现,但是现在已经闭源。  Consul Consul 是基于GO语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现和配置管理的功能。Consul 的功能都很实用,其中包括:服务注册/发现、健康检查、Key/Value 存储、多数据中心和分布式一致性保证等特性。  Consul 本身只是一个二进制的可执行文件,所以安装和部署都非常简单,只需要从官网下载后,在执行对应的启动脚本即可。  Nacos Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是 SpringCloud Alibaba 组件之一,负责服务注册发现和服务配置,可以这样认为 nacos=eureka+config  Nacos 简介 Nacos 致力于帮助我们发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助我们快速实现动态服务发现、服务配置、服务元数据及流量管理。  从上面的介绍就可以看出, nacos 的作用就是一个注册中心,用来管理注册上来的各个微服务。  Nacos 实战入门 接下来,我们就在现有的环境中加入nacos,并将我们的两个微服务注册上去。  搭建nacos环境 1、安装nacos 下载地址: https://github.com/alibaba/nacos/releases  下载zip格式的安装包,然后进行解压缩操作,下载不下来的可以直接联系阿Q:qingqing-4132  2、配置nacos 将 conf 下的 nacos-mysql.sql 导入数据库   修改 conf 下的 application.properties,添加数据库配置  spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=nacos db.password.0=nacos 1 2 3 4 5 修改bin目录下的 startup.cmd文件,将 set MODE=“cluster” 改为 set MODE=“standalone”  然后在 bin 下启动 nacos,访问地址127.0.0.0:8848/nacos,默认用户名密码nacos,可以在里边创建配置列表  3、访问nacos 打开浏览器输入http://127.0.0.1:8848/nacos/index.html,即可访问服务,默认密码是 nacos/nacos   将商品微服务注册到 nacos 接下来开始修改 shop-product 模块的代码,将其注册到 nacos 服务上  1、在 pom. xml 中添加 nacos 的依赖 <!--nacos客户端--> <dependency>      <groupId>com.alibaba.cloud</groupId>      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>      <version>2.1.0.RELEASE</version> </dependency> 1 2 3 4 5 6 2、在主类上添加 @EnableDiscoveryClient 注解 3、在 application. yml 中添加 nacos 服务的地址 spring:     cloud:         nacos:             discovery:                 server-addr: 127.0.0.1:8848 1 2 3 4 5 4、启动服务, 观察 nacos 的控制面板中是否有注册上来的商品微服务   将订单微服务注册到 nacos 接下来开始修改 shop-order 模块的代码,将其注册到 nacos 服务上,前三个步骤和商品微服务完全相同,此处直接省略。  启动之后如下   实现微服务调用 OrderController 中将请求路径写死变为从nacos中获取,代码如下:  ServiceInstance serviceInstance = discoveryClient.getInstances("shop-product").get(0); String url = serviceInstance.getHost()+":"+serviceInstance.getPort(); //通过restTemplate调用商品微服务 ShopProduct shopProduct = restTemplate.getForObject("http://"+url+"/product/"+pid, ShopProduct.class); Integer count = restTemplate.postForObject("http://"+url+"/product/reduceStock", productReduceDTO, Integer.class); 1 2 3 4 5 DiscoveryClient 是专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服务。  改好之后再次重启服务,请求地址127.0.0.1:8091/order/2,查看数据库   总结 到这儿,我们的 nacos 入门案例就结束了。下一篇将为大家带来基于nacos的负载均衡和基于feign实现微服务调用的文章,敬请期待吧!  后续的文章,我们将继续完善我们的微服务系统,集成更多的Alibaba组件。想要了解更多JAVA后端知识,请点击文末名片与我交流吧。留下您的一键三连,让我们在这个寒冷的东西互相温暖吧! ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Qingai521/article/details/135866526 
  • [技术干货] 【SpringCloud Alibaba】Nacos服务管理与Feign负载均衡-转载
     一、微服务搭建 1.1 服务提供者与服务消费者 服务提供者    服务的被调用方(即:为其他微服务提供接口的微服务) 服务消费者    服务的调用方(即:调用其他微服务接口的微服务)   就以图(仅供娱乐,无不良影响)为例搭建一个简单的微服务项目,可以看到一下项目结构:  cloud (父级项目,这样为了更好管理项目资源):  basketball (生产者)  common (公共资源) ikun (消费者)  1.2 依赖关系 1、Cloud顶级模块pom文件 (完整) :  <?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xmlns="http://maven.apache.org/POM/4.0.0"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">       <modelVersion>4.0.0</modelVersion>     <groupId>org.example</groupId>     <artifactId>cloud</artifactId>     <version>1.0-SNAPSHOT</version>     <packaging>pom</packaging>       <!--定义子模块-->     <modules>         <module>ikun</module>         <module>basketball</module>         <module>common</module>     </modules>       <!--依赖版本-->     <properties>         <spring-boot.version>2.4.2</spring-boot.version>         <spring-cloud.version>2020.0.1</spring-cloud.version>         <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>     </properties>       <dependencies>         <!--nacos服务注册-->         <dependency>             <groupId>com.alibaba.cloud</groupId>             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>         </dependency>         <!--Orika是java Bean映射框架,可以实现从一个对象递归拷贝数据至另一个对象。-->         <dependency>             <groupId>ma.glasnost.orika</groupId>             <artifactId>orika-core</artifactId>             <version>1.4.6</version>         </dependency>         <!--提供负载均衡的支持-->         <dependency>             <groupId>org.springframework.cloud</groupId>             <artifactId>spring-cloud-loadbalancer</artifactId>         </dependency>         <!--简化 Java 代码的工具库-->         <dependency>             <groupId>org.projectlombok</groupId>             <artifactId>lombok</artifactId>         </dependency>         <!--开启负载均衡-->         <dependency>             <groupId>org.springframework.cloud</groupId>             <artifactId>spring-cloud-starter-openfeign</artifactId>         </dependency>     </dependencies>       <!--声明的依赖版本号可以被子模块引用,但不会自动引入这些依赖-->     <dependencyManagement>         <dependencies>             <dependency>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-dependencies</artifactId>                 <version>${spring-boot.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>             <dependency>                 <groupId>org.springframework.cloud</groupId>                 <artifactId>spring-cloud-dependencies</artifactId>                 <version>${spring-cloud.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>             <dependency>                 <groupId>com.alibaba.cloud</groupId>                 <artifactId>spring-cloud-alibaba-dependencies</artifactId>                 <version>${spring-cloud-alibaba.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>         </dependencies>     </dependencyManagement>   </project>          在父级中指定了子模块,子模块需要引用父级模块就能同步使用父级依赖,这样就可以把所有子模块共同依赖同意管理。  2、ikun子模块pom: (basketball如同)  <?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <groupId>com.example</groupId>     <artifactId>ikun</artifactId>     <version>0.0.1-SNAPSHOT</version>       <!--引用父模块依赖-->     <parent>         <groupId>org.example</groupId>         <artifactId>cloud</artifactId>         <version>1.0-SNAPSHOT</version>     </parent>       <dependencies>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>         <!--引入公共模块-->         <dependency>             <groupId>org.example</groupId>             <artifactId>common</artifactId>             <version>1.0-SNAPSHOT</version>         </dependency>     </dependencies>       <build>         <plugins>             <plugin>                 <groupId>org.apache.maven.plugins</groupId>                 <artifactId>maven-compiler-plugin</artifactId>                 <version>3.8.1</version>                 <configuration>                     <source>1.8</source>                     <target>1.8</target>                     <encoding>UTF-8</encoding>                 </configuration>             </plugin>             <plugin>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-maven-plugin</artifactId>                 <version>${spring-boot.version}</version>                 <configuration>                     <mainClass>com.example.ikun.IkunApplication</mainClass>                 </configuration>                 <executions>                     <execution>                         <id>repackage</id>                         <goals>                             <goal>repackage</goal>                         </goals>                     </execution>                 </executions>             </plugin>         </plugins>     </build>   </project>            二、服务注册与负载均衡使用      以上的操作只是建造了一个空壳,我们需要通过一些组件来继续完善微服务体系结构。  2.1 Nacos 实现服务的注册与发现 Nacos下载:Releases · alibaba/nacos · GitHub  1、添加依赖          <dependency>             <groupId>com.alibaba.cloud</groupId>             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>         </dependency> 2、配置服务提供者,从而服务提供者可以通过 Nacos 的服务注册发现功能将其服务注册到 Nacos server 上。  basketball(服务提供者)配置Nacos server 的地址:  server:   port: 8080 spring:   cloud:     nacos:       server-addr: localhost:8848   application:     name: basketball  通过 Spring Cloud 原生注解 @EnableDiscoveryClient 开启服务注册发现功能:  package com.example.basketball;   import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;   @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class BasketballApplication {       public static void main(String[] args) {         SpringApplication.run(BasketballApplication.class, args);     }   }  3、配置服务消费者,从而服务消费者可以通过 Nacos 的服务注册发现功能从 Nacos server 上获取到它要调用的服务。  ikun(服务消费者)中配置 Nacos server 的地址:  server:   port: 8081 spring:   cloud:     nacos:       server-addr: localhost:8848   application:     name: ikun 通上在启动类上添加 Spring Cloud 原生注解 @EnableDiscoveryClient 开启服务注册发现功能。  2.2 Loadbalancer负载均衡、Feign声明式服务调用 因为两个子模块都需要此组件,所以直接在父模块cloud添加负载均衡依赖:          <!--提供负载均衡的支持-->         <dependency>             <groupId>org.springframework.cloud</groupId>             <artifactId>spring-cloud-loadbalancer</artifactId>         </dependency>           <!--远程服务通信-->         <dependency>             <groupId>org.springframework.cloud</groupId>             <artifactId>spring-cloud-starter-openfeign</artifactId>         </dependency> 在两个子模块启动类添加注释开启服务通信  @EnableFeignClients 2.3 示例综合实现 那么所有配置都已搭建好,接下来编写api接口来实现服务通信与负载均衡:  1、准备一个实体与dto类,因为本次示例并没有连接数据库,仅仅编写一个类用于实例化数据。这里我准备了一个公共模块(common)用来放置公共所需的类  package pojo.dto;   import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;   /**  * @author 云村小威  * @create 2024-01-06 15:52  */ @Data @NoArgsConstructor @AllArgsConstructor public class IkunDto {       private Long id;     private String account;     private String password;     private Integer age;     private String hobby;   }  消费者 远程调用 生产者 : 需要网络传输,使用DTO同一封装对象 原理与SpringBoot启动类相同  将DTO对象封装到公共DTO模块 为需要的项目引入公共DTO模块         <!--引入公共模块-->         <dependency>             <groupId>org.example</groupId>             <artifactId>common</artifactId>             <version>1.0-SNAPSHOT</version>         </dependency> 注意点:  不需要继承父模块(重复引用问题) 打包方式为jar 不需要添加启动类的编译   VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据 封装起来。  DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是 为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的 性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。  DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。 PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一 一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应 PO的一个(或若干个)属性。  2、服务提供者(basketball)  提供接口方法和返回结果  package com.example.basketball.controller;   import pojo.dto.IkunDto; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*;   import java.util.Map;   @Slf4j @RestController @RequestMapping("/kun") public class CXKController {         @RequestMapping("/{account}")     public String getByPath(@PathVariable String account) {         log.info("account:" + account);         return "kun : 唱";     }       @RequestMapping("/param")     public String getByParam(@RequestParam("account") String account,                              @RequestParam("password") String password) {         log.info("param:" + account + "\t" + password);         return "kun : 跳";     }       @RequestMapping("/pojo")     public String getByPojo(@RequestBody IkunDto ikunDto) {         log.info("dto:" + ikunDto);         return "kun : rep";     }       @RequestMapping("/more")     public String getByMore(@RequestBody Map<String, Object> map) {         log.info("more:" + map);         return "🏀";     }   }  3、服务消费者(ikun)   I. 创建Server,并使用Feign表示其需要远程对接的服务名称,并使用@RequestMapping表示其映射的 路径   package com.example.ikun.serice;   import pojo.dto.IkunDto; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam;   import java.util.Map;   /**  * 连接生产者 Controller  *  * @author 云村小威  * @create 2024-01-06 15:40  */ @FeignClient("basketball") //连接服务器名称(服务提供者yml设置) @RequestMapping("/kun") public interface FeginKunService {       @RequestMapping("/{account}")     public String getByPath(@PathVariable(value = "account") String account);       @RequestMapping("/param")     public String getByParam(@RequestParam("account") String account,                              @RequestParam("password") String password);       @RequestMapping("/pojo")     public String getByPojo(@RequestBody IkunDto ikunDto);       @RequestMapping("/more")     public String getByMore(@RequestBody Map<String, Object> map);   }   II. 消费者行为接口测试  package com.example.ikun.controller;   import com.example.ikun.serice.FeginKunService; import pojo.dto.IkunDto; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;   import java.util.HashMap; import java.util.Map;   @RestController public class TestController {       @Autowired     private FeginKunService kunService;       @RequestMapping("/play01")     public String play() {         return kunService.getByPath("姬霓太美");     }       @RequestMapping("/play02")     public String play02() {         return kunService.getByParam("小黑子", "123");     }       @RequestMapping("/play03")     public String play03() {         return kunService.getByPojo(new IkunDto(1L, "纯路人", "123", 5 / 2, "music"));     }       @RequestMapping("/play04")     public String play04() {         Map<String, Object> paramMap = new HashMap<>();         paramMap.put("真爱粉", new IkunDto(2L, "梅丽猫", "321", 5 / 2, "唱、跳、rep、篮球"));         return kunService.getByMore(paramMap);     }   }  III. 先启动Nacos服务、在启动项目  这里注意Nacos默认为集群模式,本次示例并没有连接数据库,所以要修改为单机模式启动  windows指令 : startup.cmd -m standalone     接着输入nacos地址进行登录,账号与密码默认为:nacos    接着就可以看到已注册的服务   最后接口测试:  2.3.1 服务注册与发现测试  Nacos服务注册与发现的流程:  1、服务注册:  在微服务启动时,它会向 Nacos 服务注册自己的信息,包括 IP 地址、端口号、服务名称等。 通过 Nacos 的客户端 SDK 或与之集成的框架(如 Spring Cloud)来完成服务注册。 2、服务发现:  当一个服务需要调用另一个服务时,它会向 Nacos 发送一个服务发现请求,请求特定服务名称的所有可用实例。 Nacos 会返回该服务名称对应的所有服务实例信息,包括 IP 地址和端口号。 调用方可以根据负载均衡策略选择一个实例进行调用,从而实现服务间的通信。 2.3.2 负载均衡测试  为了更好体现负载均衡的作用,这里将basketball与ikun两个模块进行打包运行测试   Tip:  请看上面这张动图:首先我启用了两个生产者(basketball)和一个消费者(ikun),在ikun调用basketball方法时,它是实现了负载均衡的(各自权重都为1)。而我把一个basketball关掉之后,它并立刻没有把服务下线。原因是服务它会时刻会向Nacos发送心跳证明我还活着,如果关掉某个服务,Nacos会给服务10秒的等待时间,10秒内服务没有向Nacos发送心跳就会把他下线不在使用此服务。  ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Justw320/article/details/135430167 
  • [技术干货] 如何解决Spring Cloud Gateway微服务网关Body只能读取一次的问题?
    在构建微服务架构时,Spring Cloud Gateway作为一个重要的微服务网关,经常需要在过滤器(Filter)中对POST请求的Body内容进行操作,如日志记录、签名验证和权限验证等。然而,由于Request的Body只能读取一次,如果直接在过滤器中读取而不进行封装,可能导致后续服务无法获取数据。网上搜这个问题的解决方案,大多数文章都是告诉你写一个Filter将Request的Body缓存起来。这种方法确实可以,只不过需要对代码经过充分压力测试,否则很有可能出现如下所示的堆外内存溢出问题。// 堆代码 duidaima.comreactor.netty.ReactorNetty$InternalNettyException: io.netty.util.internal.OutOfDirectMemoryError:failed to allocate实际上,Spring Cloud Gateway已经内置了AdaptCachedBodyGlobalFilter过滤器,它在Exchange中巧妙地缓存了Request的Body,避免了直接读取导致的一系列问题。这种方式更为稳妥,避免了潜在的内存溢出风险。在需要获取Body的地方,我们只需要通过以下方法即可:DataBuffer body = exchange.getAttributeOrDefault("cachedRequestBody", null);String bodyStr = body.toString(StandardCharsets.UTF_8);只不过通过源码可以看出,缓存RequestBody需要路由被标记为需要缓存,也就是this.routesToCache.containsKey(rouceId)方法必须返回true。AdaptCachedBodyGlobalFilter会监听EnableBodyCachingEvent事件,当发布该事件时就将RouteId放入routesToCache中。为了方便使用,我们可以编写一个配置类,在初始化时发布EnableBodyCachingEvent事件,将所有路由都启用缓存功能。@Configuration(proxyBeanMethods = false)@Slf4jpublic class EnableCachedBodyConfiguration { // 堆代码 duidaima.com @Resource private ApplicationEventPublisher publisher; @Resource private GatewayProperties gatewayProperties; @PostConstruct public void init() { gatewayProperties.getRoutes().forEach(routeDefinition -> { // 对 spring cloud gateway 路由配置中的每个路由都启用 AdaptCachedBodyGlobalFilter EnableBodyCachingEvent enableBodyCachingEvent = new EnableBodyCachingEvent(new Object(), routeDefinition.getId()); publisher.publishEvent(enableBodyCachingEvent); }); }}通过这种方式,我们可以更加方便地处理POST请求的Body内容,避免了一些常见的问题。在实际应用中,考虑到稳定性和性能,这种解决方案提供了一种更为可靠的选择。转载自https://www.duidaima.com/Group/Topic/JAVA/18125
  • [技术干货] Nacos有几种负载均衡策略?
    Nacos 作为目前主流的微服务中间件,包含了两个顶级的微服务功能:配置中心和注册中心。1.配置中心扫盲配置中心是一种集中化管理配置的服务,通俗易懂的说就是将本地配置文件“云端化”。这样做的好处有以下几个:集中管理配置信息:配置中心将不同服务的配置信息集中放在一起进行管理,实现了配置信息的集中存储。动态更新配置:配置中心中的配置信息可以通过操作界面或 API 进行动态更新,无需重启服务就可以应用最新的配置信息。配置信息共享:将配置集中在配置中心中,不同的服务实例可以共享同一套配置信息。配置信息安全:配置中心可以对配置信息提供安全管理、权限控制等管理功能。信息追溯:支持配置版本管理、历史记录等管理功能。当然,配置中心不可能有负载均衡的功能,所以略过,咱们直接来看注册中心。2.注册中心扫盲注册中心(Registry)是分布式系统中的一个组件,用于实现服务的注册与发现。注册中心用于管理服务实例的元数据信息,并提供服务发现和路由的功能。在微服务架构中,服务之间经常需要互相调用和通信。注册中心的作用是为服务提供一个集中管理和协调的中心,默认情况下,服务将自己的信息注册到注册中心,其他服务可以通过查询注册中心的信息来发现和调用目标服务。注册中心的核心功能包括以下几个:服务注册:服务提供者在启动时将自己的信息(比如 IP 地址、端口号、服务名称等)注册到注册中心。注册中心维护着一张服务实例的清单。服务发现:服务消费者通过向注册中心查询服务信息,获取可用的服务实例列表。通过注册中心,服务消费者能够找到并连接到目标服务。健康检查:注册中心可以定时检查服务实例的健康状态,并根据服务的状态更新服务实例的可用性。负载均衡:注册中心可以根据负载均衡策略,将请求分发给不同的服务实例,以实现负载均衡和服务高可用。服务路由:在一些高级注册中心中,还可以定义服务路由规则,将请求路由到不同的服务实例,实现更灵活的流量控制和管理。3.注册中心与负载均衡负载均衡严格的来说,并不算是传统注册中心的功能。⼀般来说服务发现的完整流程应该是先从注册中心获取到服务的实例列表,然后再根据自身的需求,来选择其中的部分实例或者按照⼀定的流量分配机制来访问不同的服务提供者,因此注册中心本身⼀般不限定服务消费者的访问策略。例如 Eureka、Zookeeper 包括 Consul,本身都没有去实现可配置及可扩展的负载均衡机制,Eureka 的负载均衡是由 Ribbon 来完成的,而 Consul 则是由 Fabio 做负载均衡。也就是说注册中心和负载均衡,其实完全属于两个不同的东西,注册中心主要提供服务的注册,以及将服务注册的列表交给消费者,至于消费者要使用哪种负载均衡策略?完全可以由自己决定。此时消费者可以通过客户端负载均衡器来实现服务的选择和调用,例如客户端负载均衡器 Ribbon 或 Spring Cloud LoadBalancer。4.客户端与服务端负载均衡客户端负载均衡器通常位于服务的消费者端,主要负责将请求合理地分发给不同的服务提供者。工作原理是客户端在发起请求前,通过负载均衡算法选择一个合适的服务实例进行请求。客户端根据服务实例的健康度、负载状况等指标来决定选择哪个服务实例。常见的客户端负载均衡器有 Ribbon、Feign 等。服务端负载均衡器通常被称为反向代理服务器或负载均衡器,它位于服务的提供者端,接收客户端的请求,并根据一定的负载均衡策略将请求分发给后端的多个服务实例。工作原理是将客户端的请求集中到负载均衡器,由负载均衡器将请求分发给多台服务提供者。常见的服务器端负载均衡器有 Nginx、HAProxy 等。客户端负载均衡 VS 服务端负载均衡客户端负载均衡器的优点是可以实现本地的负载均衡算法,避免了对注册中心的频繁调用,降低了网络开销。它的缺点是每个客户端都需要集成负载均衡器,导致代码冗余和维护复杂性。服务器负载均衡器的优点是可以集中管理请求流量,提供一致的负载均衡策略和配置,对客户端透明。它的缺点是服务器端负载均衡器通常需要独立部署和配置,增加了系统的复杂性和维护成本。并且它很可能成为整个系统的瓶颈(因为客户端需要频繁的调用),所以此时需要考虑其性能和可靠性等问题。5.Nacos和负载均衡然而 Nacos 的注册中心和传统的注册中心不太一样,例如 Eureka、Zookeeper、Consul 等。因为 Nacos 在 0.7.0 之后(包含此版本),它内置了以下两种负载均衡策略:基于权重的负载均衡策略,这个在 Nacos 服务编辑的时候也可以看到其设置:基于第三方 CMDB(地域就近访问)标签的负载均衡策略,这个可以参考官方说明文档:cid:link_0小结注册中心和负载均衡器严格意义上来说是两个东西,但 Nacos 注册中心中,内置了两种负载均衡策略:基于权重和基于 CMDB(低于就近访问)的负载均衡策略。转载自https://www.cnblogs.com/vipstone/p/17800652.html
  • [技术干货] 若依cloud-plus框架使用心得(狮子大佬写的那个)附centos7.9下minio安装教程
    最近一直想更深入的去学习使用springcloud,在网上搜了下很多大佬写的框架,然后就各种搜,看那个适合自己。虽然明知道贪多嚼不烂,但是还是想找一个技术栈比较新,比较全的框架来学习 。然后就先遇到了若依。讲真,虽然项目里面可能有些用不到。但是是真的全面。结果用了几天无意就发现了plus版本也就是狮子大佬写的那个,那个好像看大佬的对比,支持的更多,更灵活。先说说几点感受吧 。1.之前自己使用的是fastdfs,测试 生产环境部署的时候都是非常痛苦的,步骤非常之多,ruoyi-cloud-plus使用的是minio,好不好用我先不说,因为我只是自己在测试环境学习,体会不到性能的差距,但是单纯就安装部署方面来讲,2个字完爆。。。其次文档方面更是没得比 。起码我在华为云服务器上安装minio一次成功。下面插播一下安装过程 哈哈 。参考网上的链接,但是也有些链接是不能一次成功的,(别问我怎么知道的,我朋友发的链接就是让我安装出问题了)其实也不是一次出问题了,这个暂时不表,放在后面说。一、下载安装文件1、在home目录下创建minio文件夹mkdir /home/minio2、进入/home/minio 文件夹cd /home/minio3、下载文件wget 链接二、创建数据文件夹mkdir /home/minio/data三、创建日志文件touch /home/minio/log/minio.log四、启动# 赋予权限chmod 777 minio# 前台启动命令./minio server /home/minio/data# 后台启动命令nohup ./minio server --address :9000 --console-address :9010 /opt/soft/minio/data > /opt/soft/minio/log/minio.log 2>&1 &注意:按照如上启动命令启动时,访问控制台地址为ip+9010端口,且注意开放防火墙,文件服务器地址就还是9000端口上面这个地方也是真的在第一篇链接中没仔细看, 然后再加上官方文档也说的端口是9000 ,然后 我的minio也正常启动了 但是就是不能进入控制台,防火墙,服务,什么的都检查了都没办法进入,在我后来看到的教程里才看到这里是分为2个端口的,9010才是控制台端口,所以去安全策略里面添加9010端口就可以访问了。如果想要修改超管账户名和密码1、打开 /etc/profile 文件vi /etc/profile2、在文件的最末尾加上以下信息(启动的时候看提示,新版本需要用MINIO_ROOT_USER和MINIO_ROOT_PASSWORD,旧版需要用MINIO_ACCESS_KEY和MINIO_SECRET_KEY)。按 i 键后,在文档末尾输入(1)新版:export MINIO_ROOT_USER=minioadminexport MINIO_ROOT_PASSWORD=admin@111(2)旧版export MINIO_ACCESS_KEY=minioadminexport MINIO_SECRET_KEY=admin@111重点就在这里啊 , 别人发的链接只有新版的,然后我就直接使用了 但是发现并不行,后来发现用的是旧版才可以 3、保存退出(1)编辑完成后,按ctrl+c键结束编辑(2)输入:wq保存退出4、然后 刷新,重载环境变量source /etc/profile安装告一段落。继续说使用感受2.对于代码生成器的感受。可能是我一直没关注,我用了他这个代码生成器,(也不知道是若依大佬,还是狮子大佬,反正都是大佬)大佬写的代码生成器我只有2个字全面。先放图基本上对于简单业务来说是可以不用写代码的,简单的调整下样式就可以,真的是非常全面。3.nacos作为配置中心,真的感觉还是挺方便。当然刚开始也是各种踩坑项目里面改了 不知道怎么就没生效,后来才知道需要再nacos里面改,而且这个里面配置太多了 ,还是在学习中。对于配置统一管理也挺好的 。至于其他技术还是在摸索中,后续有心得了继续分享。
  • [技术干货] Nacos Auth权限
    Nacos Auth是Nacos提供的身份验证和授权模块。Nacos是一个开源的解决方案,用于动态服务发现、配置管理和服务管理。Nacos Auth可以通过启用身份验证和授权机制,确保对Nacos控制台和API的访问安全。它提供了细粒度的访问控制,以保护Nacos资源免受未经授权的访问。借助Nacos Auth,您可以配置不同的身份验证方法,例如用户名和密码认证或基于令牌的身份验证,用于对访问Nacos控制台或API的用户进行身份验证。它还支持基于角色的访问控制(RBAC),您可以将角色分配给用户,并为不同的资源定义权限。通过实施Nacos Auth,您可以确保只有经过身份验证和授权的用户可以访问您的Nacos基础架构。这有助于提高Nacos部署的安全性,并保护敏感配置数据和服务信息的安全。Nacos Auth提供以下参数来配置身份验证和授权:auth.enabled:启用或禁用Nacos Auth功能。设置为true启用,设置为false禁用。auth.identification:用于身份认证的方法。可以是password(用户名和密码认证)或token(令牌认证)。auth.password.encrypt:设置是否对密码进行加密。设置为true进行加密,设置为false不进行加密。默认为true。auth.token.ttl:令牌的过期时间(毫秒)。可以根据需要设置合适的值,默认为60000毫秒(1分钟)。auth.token.secretKey:用于生成和验证令牌的密钥。可以是任意字符串,用于加密和解密令牌。auth.token.ignoreUrls:需要忽略令牌验证的URL列表。可以配置某些URL不受令牌验证的限制。auth.rbac.enabled:启用或禁用基于角色的访问控制。设置为true启用,设置为false禁用。auth.rbac.rolePerms:定义角色和权限的映射。可以为每个角色指定一组权限。这些参数可以根据您的需求进行配置,以实现适合您的身份验证和授权策略。
  • [技术干货] Nacos Gateway参数详解
    Nacos Gateway 是基于 Nacos 服务发现和配置中心的一种开源服务网关解决方案。它提供了一种简单而强大的方式来管理和路由服务间的请求流量,同时支持动态的路由配置和鉴权,具有高度的可扩展性和灵活性。在 Nacos Gateway 中,有一些常用的参数可用于配置和定制网关的行为。下面是一些常见的参数及其详细说明:routeRules:参数类型:字符串(JSON 格式)参数说明:用于配置路由规则,指定请求的转发目标。可以配置多个路由规则。示例:[ { "predicate": { "path": "/api", "method": "GET" }, "filters": [ { "name": "StripPrefix", "args": { "parts": 1 } } ], "uri": "http://backend-service" }, { "predicate": { "path": "/api", "method": "POST" }, "filters": [ { "name": "AddRequestHeader", "args": { "name": "Content-Type", "value": "application/json" } } ], "uri": "http://backend-service" } ]说明:上述示例中设置了两个路由规则。第一个规则是将 GET 方式请求 /api 路径的请求转发到名为 http://backend-service 的服务,并剥离路径中的前缀。第二个规则是将 POST 方式请求 /api 路径的请求转发到同样的服务,并在转发请求时添加头部信息。redirectRules:参数类型:字符串(JSON 格式)参数说明:用于配置重定向规则,指定请求的重定向路径。可以配置多个重定向规则。示例:[ { "predicate": { "path": "/old", "method": "GET" }, "status": 302, "uri": "/new" } ]说明:上述示例中设置了一个重定向规则,将 GET 方式请求 /old 路径的请求重定向到 /new 路径,并返回状态码 302。globalFilters:参数类型:字符串(JSON 格式)参数说明:用于配置全局过滤器,对所有请求都生效的过滤器。可以配置多个全局过滤器。示例:[ { "name": "RateLimiter", "args": { "burstCapacity": 10, "replenishRate": 5 } } ]说明:上述示例中设置了一个全局过滤器,使用令牌桶算法进行限流。每秒钟允许发出 5 个请求,令牌桶最大容量为 10 个。consumerCluster:参数类型:字符串参数说明:用于指定服务消费者的集群名称,可以接收多个集群名称,多个集群名称之间用逗号分隔。示例:consumerCluster: cluster1,cluster2以上是一些常见的 Nacos Gateway 参数的详细说明,不同网关实现可能会有更多的参数选项和配置方式。具体可以根据实际场景和需求进行配置和定制。
总条数:55 到第
上滑加载中