• [技术干货] Spring Cloud Alibaba全家桶(一)——Spring Cloud Alibaba介绍-转载
     一、微服务介绍 1️⃣系统架构演变 随着互联网的发展,网站应用的规模也在不断的扩大,进而导致系统架构也在不断的进行变化。  从互联网早起到现在,系统架构大体经历了下面几个过程: 单体应用架构—>垂直应用架构—>分布 式架构—>SOA架构—>微服务架构,当然还有悄然兴起的Service Mesh(服务网格化)。  接下来我们就来了解一下每种系统架构是什么样子的, 以及各有什么优缺点。  🍀(1)单体应用架构  互联网早期,一般的网站应用流量较小,只需一个应用,将所有功能代码都部署在一起就可以,这样可以减少开发、部署和维护的成本。  比如说一个电商系统,里面会包含很多用户管理,商品管理,订单管理,物流管理等等很多模块,我们会把它们做成一个web项目,然后部署到一台tomcat服务器上。   优点:  项目架构简单,小型项目的话, 开发成本低 项目部署在一个节点上, 维护方便 缺点:  全部功能集成在一个工程中,对于大型项目来讲不易开发和维护 项目模块之间紧密耦合,单点容错率低 无法针对不同模块进行针对性优化和水平扩展 🍀(2)垂直应用架构  随着访问量的逐渐增大,单一应用只能依靠增加节点来应对,但是这时候会发现并不是所有的模块都会有比较大的访问 量.  还是以上面的电商为例子, 用户访问量的增加可能影响的只是用户和订单模块, 但是对消息模块的影响就比较小. 那么此时我们希望只多增加几个订单模块, 而不增加消息模块. 此时单体应用就做不到了, 垂直应用就应运而生了.  所谓的垂直应用架构,就是将原来的一个应用拆成互不相干的几个应用,以提升效率。比如我们可以将上面电商的单体 应用拆分成:  电商系统(用户管理 商品管理 订单管理) 后台系统(用户管理 订单管理 客户管理) CMS系统(广告管理 营销管理) 这样拆分完毕之后,一旦用户访问量变大,只需要增加电商系统的节点就可以了,而无需增加后台和CMS的节点。   优点:  系统拆分实现了流量分担,解决了并发问题,而且可以针对不同模块进行优化和水扩展 一个系统的问题不会影响到其他系统,提高容错率 缺点:  系统之间相互独立, 无法进行相互调用 系统之间相互独立, 会有重复的开发任务 🍀(3)分布式架构  当垂直应用越来越多,重复的业务代码就会越来越多。这时候,我们就思考可不可以将重复的代码抽取出来,做成统一的业务层作为独立的服务,然后由前端控制层调用不同的业务层服务呢?  这就产生了新的分布式系统架构。它将把工程拆分成表现层和服务层两个部分,服务层中包含业务逻辑。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。   优点:  抽取公共的功能为服务层,提高代码复用性 缺点:  系统间耦合度变高,调用关系错综复杂,难以维护 🍀(4)SOA架构  在分布式架构下,当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心对集群进行实时管理。此时,用于资源调度和治理中心(SOA Service Oriented Architecture)是关键。   优点:  使用治理中心(ESB\dubbo)解决了服务间调用关系的自动调节 缺点:  服务间会有依赖关系,一旦某个环节出错会影响较大( 服务雪崩 ) 服务关系复杂,运维、测试部署困难 🍀(5)微服务架构  微服务架构在某种程度上是面向服务的架构SOA继续发展的下一步,它更加强调服务的"彻底拆分"。   微服务架构与SOA架构的不同  微服务架构比 SOA架构粒度会更加精细,让专业的人去做专业的事情(专注),目的提高效率,每个服务于服务之间互不影响,微服务架构中,每个服务必须独立部署,微服务架构更加轻巧,轻量级。  SOA 架构中可能数据库存储会发生共享,微服务强调独每个服务都是单独数据库,保证每个服务于服务之间互不影响。  项目体现特征微服务架构比 SOA 架构更加适合与互联网公司敏捷开发、快速迭代版本,因为粒度非常精细。  优点:  服务原子化拆分,独立打包、部署和升级,保证每个微服务清晰的任务划分,利于扩展 微服务之间采用Restful等轻量级http协议相互调用 缺点:  分布式系统开发的技术成本高(容错、分布式事务等) 复杂性更高。各个微服务进行分布式独立部署,当进行模块调用的时候,分布式将会变得更加麻烦。 2️⃣微服务架构介绍 作者:  Martin Fowler  论文原文:https://martinfowler.com/articles/microservices.html 论文翻译:http://blog.cuicc.com/blog/2015/07/22/microservices  他说微服务其实是一种架构风格,我们在开发一个应用的时候这个应用应该是由一组小型服务组成,每个小型服务都运行在自己的进程内;小服务之间通过HTTP的方式进行互联互通。    微服务架构的常见问题  一旦采用微服务系统架构,就势必会遇到这样几个问题:  这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除]) nacos 这么多小服务,他们之间如何通讯?(restful rpc dubbo feign) httpclient(“url”,参数), springBoot restTemplate(“url”,参数) , feign 这么多小服务,客户端怎么访问他们?(网关) gateway 这么多小服务,一旦出现问题了,应该如何自处理?(容错) sentinel 这么多小服务,一旦出现问题了,应该如何排错? (链路追踪) skywalking 对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。   3️⃣常见微服务架构 🍀(1)dubbo: zookeeper +dubbo + SpringMVC/SpringBoot  配套 通信方式:rpc 注册中心:zookeeper / redis 配置中心:diamond 🍀(2)SpringCloud:全家桶+轻松嵌入第三方组件(Netflix)  配套 通信方式:http restful 注册中心:eruka / consul 配置中心:config 断路器:hystrix 网关:zuul 分布式追踪系统:sleuth + zipkin 🍀(3)SpringCloud Alibaba  Spring Cloud 以微服务为核心的分布式系统构建标准 “分布式系统中的常见模式”给了 Spring Cloud 一个清晰的定位,即“模式”。也就是说 Spring Cloud 是针对分布式系统开发所做的通用抽象,是标准模式的实现。这个定义非常抽象,看完之后并不能知道 Spring Cloud 具体包含什么内容。再来看一下 Spring 官方给出的一个 High Light 的架构图,就可以对这套模式有更清晰的认识:   可以看到这个图中间就是各个 Microservice,也就是我们的这个微服务的实现,周边周围的话就是去围绕这个微服务来去做各种辅助的信息事情。例如分布式追踪、服务注册、配置服务等,都绕微服务运行时所依赖的必不可少的的支持性功能。我们可以得出这样一个结论:Spring Cloud 是以微服务为核心的分布式系统的一个构建标准。  二、Spring Cloud Alibaba介绍 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发微服务架构的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发微服务架构。  依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。  根据 Jakarta 2019 年的调研报告,Spring Boot 拥有非常高的占比。熟悉 Java 语言的同学,应该对 Spring 框架都不会陌生。其倡导的依赖倒置、面向切面编程等特性已经形成了 Java 语言的事实标准,几乎所有三方框架都会提供对 Spring 框架的支持。   1️⃣Spring Cloud Alibaba 的定位 既然说 Spring Cloud 是标准,那么自然少不了针对标准的实现。这里,为大家介绍下 Spring Cloud Alibaba 这套实现。先给出下面这张图帮助大家理解 Spring Cloud Alibaba 的定位:   这里给大家这么一个公式,这个叫做:“3 加 2”。  3 指的就是图中深色的部分,其实它就是 Spring Cloud 标准,一共有 3 层。中间颜色最深的部分就是及整个微服务最核心的内容,包括了“ RPC调用”以及“服务注册与发现”。第二层,也就是围绕着核心的这一圈,是一些辅助微服务更好的工作功能,包括了负载均衡、路由、网关、断路器,还有就是追踪等等这些内容。再外层的话,主要是一些分布式云环境里通用能力。  2 指的就是上图中最外面这一圈。这一部分就是这个我们 Spring Cloud Alibaba 的一个定义,它其实包含两个部分的内容:右上部分是对于 Spring Cloud 标准的实现。例如,我们通过 Dubbo 实现了 RPC 调用功能,通过 Nacos 实现了“服务注册与发现”、“分布式配置”,通过 Sentinel 实现了断路器等等,这里就不一一列举了。  左下部分是我们 Spring Cloud Alibaba 对阿里云各种服务的集成。可能很多小伙伴会有这样的一个问题:为什么要加上这一部分呢?此时回头审视一下 Spring Cloud ,它仅仅是一个微服务的一个框架。但是在实际生产过程中,单独使用微服务框架其实并不足以支撑我们去构建一个完整的系统。所以这部分是用阿里帮助开发者完成微服务以外的云产品集成的功能。  这里可能会很多小伙伴会有这么一个担心:是不是使用了 Spring Cloud Alibaba,就会被阿里云平台绑定呢?这是不会的。为什么这么说呢?如上面说的,“3 加 2”中的 2 是被分为两个部分的。其中对 Spring Cloud 的实现是完全独立的,开发者可以只是用这部分实现运行在任何云平台中。当然,另一部分,由于天然是对阿里云服务的集成,这部分是和平台相关的。这里给开发者充分的自由,选择只是用其中的部分还是全部产品。当然,我们也非常欢迎开发者选择使用阿里云的全套服务,我们也会尽量保证使用整套产品时的连贯性与开发的便利性。  2️⃣Spring Cloud 各套实现对比 Spring Cloud 作为一套标准,它的实现肯定不止一套,那么各套实现都有什么区别呢?我们来一起看一下下面这张图:   可以发现 Spring Cloud Alibaba 是所有的实现方案中功能最齐全的。尤其是在 Netflix 停止更新了以后,Spring Cloud Alibaba 依然在持续更新和迭代。    从 18 年 7 月份 Spring Cloud Alibaba 正式提交代码开始,就得到了大家广泛的关注。截止今天,Spring Cloud Alibaba 一共获得了超过了 1.5 万的 star 数,已经的领先于所有其他实现的总和。  3️⃣Spring Cloud Alibaba 生态 可以看到除了围绕着 Spring Cloud 的标准实现以外,还有包括的数据、资源、消息、缓存等各种类型的服务。在不同类型的服务下,也有很多具体的产品可供用户选择。   这里罗列典型而非全部产品。更多的内容,可以参考阿里云官网。  三、Spring Cloud Alibaba环境搭建 SpringCloud Alibaba 依赖 Java 环境来运行。还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用:  64 bit JDK 1.8+;下载 & 配置。 1.8.0_131 Maven 3.2.x+;下载 & 配置。 3.6.1 (1)基于SpringBoot的父maven项目  (2)创建2个服务(订单服务和库存服务)  版本说明:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E  Spring Cloud Alibaba:2.2.5.RELEASE Spring Boot :2.3.2.RELEASE Spring Cloud:Hoxton.SR8  父pom如下:  <?xml version="1.0" encoding="UTF‐8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance" 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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring‐boot‐starter‐parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> <!‐‐ lookup parent from repository ‐‐> </parent> <groupId>com.tuling.mall</groupId> <artifactId>vip‐spring‐cloud‐alibaba</artifactId> <version>0.0.1‐SNAPSHOT</version> <name>vip‐spring‐cloud‐alibaba</name> <packaging>pom</packaging> <description>Demo project for Spring Cloud Alibaba</description>  <properties> <java.version>1.8</java.version> <spring‐cloud.version>Hoxton.SR8</spring‐cloud.version> <spring‐cloud‐alibaba.version>2.2.5.RELEASE</spring‐cloud‐alibaba.version> </properties>  <dependencyManagement> <dependencies> <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>  ———————————————— 版权声明:本文为CSDN博主「小新要变强」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_42146402/article/details/129207664 
  • [技术干货] Java的数据类型与变量-转载
     🍊数据类型 在Java中数据类型主要分为两类:基本数据类型和引用数据类型。本文来介绍基本数据类型。 基本数据类型有:整型、浮点型、字符型以及布尔型 (这看起来和C语言一样,还挺亲切的)  🍌整型 Java中的整型有:byte,short,int,long  与C语言相比,short和int没啥变化。 有变化的点在于,多了一个byte,叫做字节型,它占一个字节,很像C语言中的char,它的范围也是从-128~127。(待会下面会对这个范围作一些说明) 同时我们不难发现,Java没有long long,因为它的long(长整型)的大小就是8个字节了,不像C语言的long有时是4个字节,而有时是8。这也正引出一个重要的点:Java的数据类型在32位和64位环境下的大小都是一样的,这保证了在不同平台的可移植性。  🥝byte的范围 (其实我之前学C语言的时候char的取值范围没有完全弄懂hhh) 我们以前学过:数据在内存中的存储是补码。数据的二进制序列中最高位是符号位。因为C语言和Java只是语言不同而已,所以它们在数据存储方面其实是大同小异的。 Java中的基本数据类型没有“无符号”这一概念,也就是说都是有符号的,必定有符号位。  先来看正数的情况,易知只要数值位都是1就可以取到最大值127。 负数的情况有点小复杂,按理来说负数最小值是-127,即11111111。至于-128,其实是人为规定的,怎么规定呢?127的源码和1的源码相加就是1000 0000,规定这个数就是-128(就最高位的1同时作为数值位和符号位这样子)  然后还有两件事: 第一,我们判定一个数的大小(这里指的是十进制大小)可以看源码,也可以看补码,但是看源码会比较直观,因为你可以直接算出来。 第二,计算机中各种运算主要是以补码参与运算。  🍌浮点型 Java浮点型也是float和double。float大小为4个字节;double为8个字节。 我们在写代码的时候如果写  float a = 0.5; 1 那编译器会报错,因为Java中浮点数默认是double类型的,而a是float,自然报错了(而C语言就不会这样,这也体现了Java的严谨)。如果你要让a就是float类型的话,就在后面加个f:  float a = 0.5f; 1 🥝浮点数相关运算 这部分的规则也是和C语言的差不多,在此不多赘述(其实是我懒得写 )。但仍要补充一些要点:  ①对于整型的除法运算,若想转化为浮点数的除法运算,则在分子or分母乘0.1就ok了。 ②Java中的小数并没有精确的位数,哪怕你在初始化一个浮点数变量时给它一个有限位数的初始值,比如float a = 1.1,是两位小数,但是你去打印a*a,会发现结果不是1.21。  🍊字符类型 计算机中的字符本质上是一个整数。 C 语言中使用 ASCII 表示字符,而 Java 中使用 Unicode 表示字符。因此一个字符占用两个字节,表示的字符种类更多,包括中文。  char c1 = 'A'; // 大写字母 char c2 = '1'; // 数字字符  System.out.println(c1); System.out.println(c2);  char c3 = '帅'; System.out.println(c3); 1 2 3 4 5 6 7 8 这里先简单介绍一下,后续会有专门的章节来讲字符和字符串的。  🍊布尔类型 布尔类型(boolean)常用来表示真假,它只有两种取值,true 表示真,false 表示假。 Java 的 boolean 类型和 int 不能相互转换,不存在1表示 true,0表示false这样的用法!!! 然后值得注意的是,Java虚拟机规范中,并没有明确规定boolean占几个字节。  🍊类型转换 (Java 作为一门强类型编程语言,当不同类型之间的变量相互赋值的时候,会有比较严格的校验。) 在Java中,当参与运算数据类型不一致时,就会进行类型转换。Java中类型转换主要分为两类:自动类型转换(隐式)和强制类型转换(显式)。  🍌自动类型转换(隐式) 代码不需要经过任何处理,在代码编译时,编译器会自动进行处理。 特点:数据范围小的转为数据范围大的时会自动进行。  int a = 100; long b = 10L; b = a; // a和b都是整形,a的范围小,b的范围大,当将a赋值给b时,编译器会自动将a提升为long类型,然后赋值 a = b; // 编译报错,long的范围比int范围大,会有数据丢失,不安全 1 2 3 4 ❓然后不知道你有没有发现这样一个问题,为啥我把byte或short赋为整型值的时候编译器不会报错呢?比如byte = 11; short = 20。  因为Java规定:对于byte,short这两种大小小于4个字节的类型,你对这两种类型的变量进行赋值的时候,只要赋的值的大小不超过这个类型的范围,那么这个值就不会被解析为整型。 比如byte a = 300,因为300已经超过byte的范围,那么此时300就会被当做整型,就会报错了。  🍌强制类型转换(显式) 这个和C语言的差不多,打个比方,强制类型转换就像你现在有一条2米的杆子,你要放进一个1米长的箱子,就只能把它砍短咯。举一些例子让你回忆一下:  int a = 10; long b = 100L; b = a; // int-->long,数据范围由小到大,隐式转换 a = (int)b; // long-->int, 数据范围由大到小,需要强转,否则编译失败  float f = 3.14F; double d = 5.12; d = f; // float-->double,数据范围由小到大,隐式转换 f = (float)d; // double-->float, 数据范围由大到小,需要强转,否则编译失败  a = d; // 报错,类型不兼容 a = (int)d; // int没有double表示的数据范围大,需要强转,小数点之后全部丢弃  byte b1 = 100; // 100默认为int,没有超过byte范围,隐式转换 byte b2 = (byte)257; // 257默认为int,超过byte范围,需要显示转换,否则报错  boolean flag = true; a = flag; // 编译失败:类型不兼容 flag = a; // 编译失败:类型不兼容  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 总结一下就是:  不同数字类型的变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型 如果需要把范围大的类型赋值给范围小的, 需要强制类型转换, 但是可能精度丢失 将一个字面值常量进行赋值的时候, Java 会自动针对数字范围进行检查 强制类型转换不一定能成功,不相干的类型不能互相转换 🍊类型提升 不同类型的数据之间相互运算时,数据类型小的会被提升到数据类型大的。 ①int与long之间:int会被提升为long  int a = 10; long b = 20; int c = a + b; // 编译出错: a + b==>int + long--> long + long 赋值给int时会丢失数据 long d = a + b; // 编译成功:a + b==>int + long--->long + long 赋值给long 1 2 3 4 第三行代码中,a类型提升之后变为long类型,此时long+long结果为long,不能用int类型接收。  ②byte与byte的运算  byte a = 10; byte b = 20; byte c = a + b; System.out.println(c); 1 2 3 4 这段代码编译也会报错,a和b虽然都是byte,但是计算 a + b 会先将 a和b 都提升成 int,再进行计算,得到的结果也是 int,此时赋给c,就会出错。  byte和short这种低于4个字节的类型,会先提升成 int,再参与计算。 原因:计算机的 CPU 通常是按照4个字节为单位从内存中读写数据,为了硬件上实现方便,所以在计算时把它们给提升了。  🍡写在最后 如果你觉得本文写得还不错,不妨点赞收藏关注三连呗! 如果发现本文有不足或不妥之处,欢迎指出,我将第一时间改正。 ———————————————— 版权声明:本文为CSDN博主「Ice_Sugar_7」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Ice_Sugar_7/article/details/134015650 
  • [技术干货] Java实验--机动车-转载
     一、实验目的  本实验的目的是让学生使用类来封装对象的属性和功能。  三、实验要求  编写一个简单的Java应用程序,该程序有两个类:Vehicle(用于刻画机动车)和User(主类)。具体要求如下:  Vehicle类有个double类型的变量speed,用于刻画机动车的速度,一个int型变量power,用于刻画机动车的功率。类中定义了speedUp(int s)方法,体现机动车有加速功能;定义了speedDown(int d)方法,体现机动车有减速功能;定义了setPower(int p)方法,用于设置机动车的功率;定义了getPower()方法,用于获取机动车的功率。机动车的UML图如图4.2所示。 在主类User的 main()方法中用Vehicle为创建对象,并让该对象调用方法设置功率,演示加速和减速功能。 四、程序效果示例  程序运行效果如图4.3所示。    图4.3 Vehicle类创建对象  运行结果如下:  五、程序模板  请按模板要求,将【代码】替换为Java程序代码。  【Vehicle.java】  public class Vehicle {         double speed;//声明double型变量speed,刻画速度         int power;  //声明int型变量power,刻画功率       void speedUp(int s) {         speed = s+speed;//将参数s的值与成员变量speed的和赋值给成员变量speed     }     void speedDown(int d) {         speed= speed-d; //将成员变量speed与参数d的差赋值给成员变量speed     }     void setPower(int p) {         power = p; //将参数p的值赋值给成员变量power     }     int getPower() {         return  power; //返回成员变量power的值     }     double getSpeed() {         return speed;     }     }  【User.java】  public class User {       public static void main(String args[]) {           Vehicle car1, car2;         car1 = new Vehicle();  //使用new运算符和默认的构造方法创建对象car1         car2 = new Vehicle();//使用new运算符和默认的构造方法创建对象car2           car1.setPower(128);           car2.setPower(76);           System.out.println("car1的功率是:"+car1.getPower());           System.out.println("car2的功率是:"+car2.getPower());         car1.speedUp(80);//car1调用speedUp()方法将自己的speed的值增加80         car2.speedUp(80);//car2调用speedUp()方法将自己的speed的值增加80           System.out.println("car1目前的速度:"+car1.getSpeed());           System.out.println("car2目前的速度:"+car2.getSpeed());           car1.speedDown(10);           car2.speedDown(20);           System.out.println("car1目前的速度:"+car1.getSpeed());           System.out.println("car2目前的速度:"+car2.getSpeed());       }   }  五、实验后的练习  (1)改进speedUp()方法,使得Vehicle类的对象在加速时不能将speed值超过200。  void speedUp(int s) {       if (s+speed > 200){           this.speed = 200;       }else {           this.speed = s+speed;       };//将参数s的值与成员变量speed的和赋值给成员变量speed   } 2. 改进speedDown()方法,使得Vehicle类的对象在减速时不能将speed值小于0。  (4)void speedDown(int d) {     if(speed-d < 0){         this.speed=0;     }     else {         this.speed= speed-d; //将成员变量speed与参数d的差赋值给成员变量speed     } } (3)增加一个刹车方法void brake(),Vehicle类的对象调用它能将speed的值变成0。  void brake(){       this.speed = 0;   } 调用brake:  car1.brake();   System.out.println("car1目前的速度:"+car1.getSpeed()); 结果如下: ———————————————— 版权声明:本文为CSDN博主「刘涧茗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/sziit1609030127/article/details/129509725 
  • [技术干货] Android连接蓝牙设备问题(android.permission.BLUETOOTH)-转载
     近期遇到一个问题,之前发布的APP连接蓝牙都是正常的,现在有人反映连不上了。经过测试发现:android 12 和 harmonyOS 3.0.0 都会有这个问题,而之前的版本就不会有这个。          经过网上一番查找,原来是因为最近Google发布的Android 12,新引入了 BLUETOOTH_SCAN、BLUETOOTH_CONNECT、BLUETOOTH_ADVERTISE 三个权限。、          从Android 12开始,过去的蓝牙权限被拆分成了3个新的权限,并且全都是运行时权限(需要动态申请):  BLUETOOTH_SCAN 用于使用蓝牙扫描附件其他的蓝牙设备 BLUETOOTH_ADVERTISE 用于允许当前的设备被其他的蓝牙设备所发现 BLUETOOTH_CONNECT 用于连接之前已经配对过的蓝牙设备         这3个权限都是从Android 12系统才开始有的,所以为了能够兼容过去的老版本,建议在AndroidManifest.xml中这样声明:      <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"  android:maxSdkVersion="30"/>     <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />     <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />     <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />         新增的3个蓝牙权限都是运行时权限,因此只在AndroidManifest.xml中声明是没有用的,还要在代码中动态申请权限才行。必须先在应用中用户明确批准使用,然后才能查找蓝牙设备、使某个设备可被其他设备检测到,或者与已配对的蓝牙设备通信。          具体的申请方法如下:首先要判断当前的系统版本,只有当Android 12及以上系统时,才应该去请求新增的蓝牙权限。(PS:3个权限都属于同一个权限组,因此理论上只要申请一个权限,另外2个也就自动授权了。)  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {             String[] permission = checkSelfPermissionArray(this, new String[]{                     Manifest.permission.BLUETOOTH_SCAN,                     Manifest.permission.BLUETOOTH_ADVERTISE,                     Manifest.permission.BLUETOOTH_CONNECT});             if (permission.length > 0) {                 ActivityCompat.requestPermissions(this, permission, 102);             }         } 注意:  之前的Android系统中有一个很奇怪的现象,当我们在应用中使用蓝牙扫描附件设备的时候,需要申请地理位置权限。蓝牙权限并不是运行时权限,但地理位置权限却是。 ———————————————— 版权声明:本文为CSDN博主「老杜_d」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/minusn/article/details/128660803 
  • [技术干货] java定义能计算圆的面积、周长的类 Circle-转载
     定义能计算圆的面积、周长的类 Circle,半径r为私有数据变量,其值由类circle的构造方法初始化,类circle提供读取半径r的方法getr();t算面积的方法area();计算周长的方法circlelength()。再定义类Circlecomputer,其包含程序运行所需的方法main,请设计程序完成初始化数据变量和计算圆的面积、周长并打印出结果。  以下是Java代码实现:  public class Circle {     private double r;          public Circle(double r) {         this.r = r;     }          public double getr() {         return r;     }          public double area() {         return Math.PI * r * r;     }          public double circlelength() {         return 2 * Math.PI * r;     } }   public class Circlecomputer {     public static void main(String[] args) {         Circle circle = new Circle(3.0); // 初始化圆的半径为3.0         System.out.println("半径为:" + circle.getr());         System.out.println("面积为:" + circle.area());         System.out.println("周长为:" + circle.circlelength());     } } 在Circle类中,我们定义了私有数据变量r,通过构造方法初始化,并提供了读取半径r的方法getr()、计算面积的方法area()和计算周长的方法circlelength()。  在Circlecomputer类中,我们创建一个Circle对象并将半径初始化为3.0,然后调用Circle对象的方法计算面积和周长,并打印输出结果。 ———————————————— 版权声明:本文为CSDN博主「鱼弦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/feng1790291543/article/details/131311635 
  • [其他] java实现流媒体服务器
    Java实现流媒体服务器的方案随着互联网的快速发展,流媒体技术在音视频领域得到了广泛的应用。流媒体服务器作为流媒体技术的核心组成部分,可以将音视频数据实时传输给用户。本文将介绍如何使用Java实现一个流媒体服务器的方案。选择合适的流媒体框架Java中有许多成熟的流媒体框架可供选择,如Red5、Darwin Streaming Server、JWPlayer等。这些框架提供了丰富的功能和良好的性能,可以帮助我们快速搭建流媒体服务器。本文将以Red5为例,介绍如何实现一个流媒体服务器。安装和配置Red5首先,我们需要下载并安装Red5服务器。可以从官方网站下载最新版本的Red5。安装过程非常简单,只需解压缩下载的文件,然后运行其中的red5.bat(Windows系统)或run.sh(Linux系统)即可启动Red5服务器。创建流媒体应用接下来,我们需要创建一个流媒体应用。在Red5中,流媒体应用是通过扩展名为.r303的配置文件来定义的。我们可以使用文本编辑器创建一个名为mystream.r303的文件,然后添加以下内容:<?xml version="1.0" encoding="UTF-8"?> <configuration> <node-list> <node name="mystream" description="My Stream Application" type="org.red5.server.stream.Application"> <param name="playback" value="false"/> <param name="record" value="false"/> <param name="preview" value="true"/> <param name="publish" value="true"/> <param name="application" value="mystream"/> <param name="name" value="mystream"/> <param name="autoStart" value="true"/> <param name="bufferTime" value="4000"/> <param name="maxBandwidth" value="1000000"/> <param name="minBandwidth" value="10000"/> <param name="audioBitrate" value="96"/> <param name="videoBitrate" value="128"/> <param name="audioSampleRate" value="44100"/> <param name="videoFrameRate" value="30"/> <param name="keyframeInterval" value="25"/> <param name="metadataInterval" value="25"/> <param name="segmentLength" value="3600"/> <param name="segmentGap" value="60"/> <param name="location" value="/mystream"/> </node> </node-list> </configuration>这个配置文件定义了一个名为mystream的流媒体应用,设置了各种参数,如音频和视频比特率、帧率等。保存文件后,将其放置在Red5服务器的配置目录下(默认为conf)。编写Java代码处理流媒体数据为了处理流媒体数据,我们需要编写一个Java类,继承自org.red5.server.stream.ClientBroadcastStream类。在这个类中,我们可以重写onPublish方法,用于处理推流事件;重写onPlay方法,用于处理播放事件;重写其他方法,以处理其他事件。以下是一个简单的示例:package com.example; import org.red5.server.api.stream.ClientBroadcastStream; import org.red5.server.api.stream.IStreamPacket; import org.red5.server.stream.ClientBroadcastStream; import org.red5.server.stream.scope.IScope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MyStream extends ClientBroadcastStream { private static final Logger log = LoggerFactory.getLogger(MyStream.class); private IScope scope; private String streamName; private int audioChannels; private int audioSampleRate; private int audioBitRate; private int videoWidth; private int videoHeight; private int videoCodecId; private boolean isAudioActive; private boolean isVideoActive; private long lastAudioReceived; private long lastVideoReceived; private long lastAudioSent; private long lastVideoSent; private long lastAudioKeyFrameSent; private long lastVideoKeyFrameSent; private long lastMetadataSent; private long segmentStartTime; private long segmentDuration; private boolean isRecording; private boolean isPreviewing; // ...其他成员变量和方法... }在这个类中,我们可以处理各种事件,如接收到音频和视频数据、连接断开等。同时,我们还可以根据需要添加其他功能,如录制、预览等。
  • [内容拦截申诉] 发的帖子 哪里有问题??
    SpringBoot有几种获取Request对象的方法_CodeArts_华为云论坛 (huaweicloud.com)
  • [技术干货] 线程池底层核心接口简析
     ThreadPoolExcecutor构造方法参数  1.继承关系  Executor            ↑  ExecutorService             ↑  AbstractExecutorService              ↑  ThreadPoolExecutor  public ThreadPoolExecutor(int corePoolSize,int maxNumPoolSize, long ReepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectExecutionHandler handler){  }  corePoolSize:线程池核心线程数  maxNumPoolSize:线程池最大数  ReepAliveTime:空闲线程存活时间  unit:时间单位  workQueue:线程池所使用的缓冲队列  threadFactory:线程池创建使用的工厂  handler:线程池对拒绝任务的处理策略     特性一:当池中正在运行的线程数(包括空闲线程),小于corePoolSize时,从线程池中取线程执行任务。  特性二:当池中正在运行的线程数大于等于corePoolSize时,新插入的任务进入workQueue排队。  特性三:当队列里的任务达到上限,并且池中正在运行的线程数小于maxNumPoolSize,对于新加入的任务,新建线程。  特性四:当队列里的任务数达到上限,并且池中正在运行的线程数等于maxNumPoolSize,对于新加入的任务,执行拒绝策略(默认策略跑出RejectdExcutionException) ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/105604858 
  • [技术干货] Java四种线程池的使用
     为了减少创建和销毁线程的次数,让每个线程可以多次使用,可根据系统情况调整执行的线程数量,防止消耗过多内存,所以我们可以使用线程池.  Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。     (1) newCachedThreadPool 创建一个可缓存线程池,如果线线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,若无可回收,则新建线程。示例代码如下:  Java代码    package test;   import java.util.concurrent.ExecutorService;   import java.util.concurrent.Executors;   public class ThreadPoolExecutorTest {    public static void main(String[] args) {     ExecutorService cachedThreadPool = Executors.newCachedThreadPool();     for (int i = 0; i < 10; i++) {      final int index = i;      try {       Thread.sleep(index * 1000);      } catch (InterruptedException e) {       e.printStackTrace();      }      cachedThreadPool.execute(new Runnable() {       public void run() {        System.out.println(index);       }      });     }    }   }      线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。   (2) newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:  Java代码    package test;   import java.util.concurrent.ExecutorService;   import java.util.concurrent.Executors;   public class ThreadPoolExecutorTest {    public static void main(String[] args) {     ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);     for (int i = 0; i < 10; i++) {      final int index = i;      fixedThreadPool.execute(new Runnable() {       public void run() {        try {         System.out.println(index);         Thread.sleep(2000);        } catch (InterruptedException e) {         e.printStackTrace();        }       }      });     }    }   }     因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。 定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()     (3)  newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:  Java代码    package test;   import java.util.concurrent.Executors;   import java.util.concurrent.ScheduledExecutorService;   import java.util.concurrent.TimeUnit;   public class ThreadPoolExecutorTest {    public static void main(String[] args) {     ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);     scheduledThreadPool.schedule(new Runnable() {      public void run() {       System.out.println("delay 3 seconds");      }     }, 3, TimeUnit.SECONDS);    }   }     表示延迟3秒执行。  定期执行示例代码如下:  Java代码    package test;   import java.util.concurrent.Executors;   import java.util.concurrent.ScheduledExecutorService;   import java.util.concurrent.TimeUnit;   public class ThreadPoolExecutorTest {    public static void main(String[] args) {     ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);     scheduledThreadPool.scheduleAtFixedRate(new Runnable() {      public void run() {       System.out.println("delay 1 seconds, and excute every 3 seconds");      }     }, 1, 3, TimeUnit.SECONDS);    }   }     表示延迟1秒后每3秒执行一次。     (4) newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:  Java代码    package test;   import java.util.concurrent.ExecutorService;   import java.util.concurrent.Executors;   public class ThreadPoolExecutorTest {    public static void main(String[] args) {     ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();     for (int i = 0; i < 10; i++) {      final int index = i;      singleThreadExecutor.execute(new Runnable() {       public void run() {        try {         System.out.println(index);         Thread.sleep(2000);        } catch (InterruptedException e) {         e.printStackTrace();        }       }      });     }    }   }     结果依次输出,相当于顺序执行各个任务。     ExcutorService中的excutor和submit方法的区别 两者都是将一个线程任务添加到线程池中并执行; 1、excutor没有返回值,submit有返回值,并且返回执行结果Future对象 2、excutor不能提交Callable任务,只能提交Runnable任务,submit两者任务都可以提交 3、在submit中提交Runnable任务,会返回执行结果Future对象,但是Future调用get方法将返回null(Runnable没有返回值)    使用场景: 使用多线程任务校验被保人数据; 1、每个被保人都是一个线程任务, 2、每个线程任务执行完都需要告诉主线程执行成功还是失败 3、这里需要submit提交Callable任务返回Future对象,并通过Future.get方法来获取执行结果  本地代码示例: 1.传统spring项目:  调用接口:  public class HmoPushOrderServiceImpl implements HmoPushOrderService {     @Override  public ResponseDTO<Boolean> pushHmoUserToPingAn() {         ResponseDTO<Boolean> res = new ResponseDTO<Boolean>();         ExecutorService threadPool = PingAnUserActivateTaskThreadPool.getInstance().getThreadPool();         threadPool.submit(new PingAnActivateThreadTask());         res.setResult(ResponseCode.SUCCESS, "触发成功", Boolean.TRUE);         return res;     }  异步线程池创建类:  import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;   public class PingAnUserActivateTaskThreadPool {       // 最大线程数     private static final int MAX_QUEUE_SIZE = 5;       private static ExecutorService exc = null;       private static volatile PingAnUserActivateTaskThreadPool instance = null;       private PingAnUserActivateTaskThreadPool() {         exc = Executors.newFixedThreadPool(MAX_QUEUE_SIZE);     }       /**      *      * @return      */     public static PingAnUserActivateTaskThreadPool getInstance() {         if (instance == null) {             synchronized (PingAnUserActivateTaskThreadPool.class) {                 if (instance == null) {                     instance = new PingAnUserActivateTaskThreadPool();                 }             }         }         return instance;     }       public ExecutorService getThreadPool() {         return exc;     }   } 任务执行类:   public class PingAnActivateThreadTask implements Runnable {       public PingAnActivateThreadTask() {         hmoPushOrderManager = SpringContextUtil.getBean("hmoPushOrderManager");         msgSender = SpringContextUtil.getBean("msgSender");         configUtils = SpringContextUtil.getBean("configUtils");         apiUserBindManager = SpringContextUtil.getBean("apiUserBindManager");     }       @Override     public void run() {         ...     } } 2.spring-boot项目:  业务Controller:  @RestController @RequestMapping("hfxyk/") public class HfxykDataController {    @Resource     private ExecutorService taskExecutor;       taskExecutor.submit(new Runnable() {                 @Override                 public void run() {                        ...                 }     } }  线程池配置公用类:  import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;   import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;     @Configuration public class ThreadPoolConfig {       /**      * 公共业务线程池      *      * @return      */     @Bean(name = "taskExecutor")     public ExecutorService executorService() {         ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("executor-hfxyk-pool-%d").build();         return new ThreadPoolExecutor(2, 8, 1000, TimeUnit.SECONDS,                 new LinkedBlockingQueue<>(20), namedThreadFactory, new ThreadPoolExecutor.DiscardPolicy());     } }   注意:DiscardPolicy策略:对拒绝任务直接无声抛弃,没有异常信息。     你可以使用JDK自带的监控工具来监控我们创建的线程数量,运行一个不终止的线程,创建指定量的线程,来观察: 工具目录:C:\Program Files\Java\jdk1.6.0_06\bin\jconsole.exe 运行程序做稍微修改:  Java代码    package test;   import java.util.concurrent.ExecutorService;   import java.util.concurrent.Executors;   public class ThreadPoolExecutorTest {    public static void main(String[] args) {     ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();     for (int i = 0; i < 100; i++) {      final int index = i;      singleThreadExecutor.execute(new Runnable() {       public void run() {        try {         while(true) {          System.out.println(index);          Thread.sleep(10 * 1000);         }        } catch (InterruptedException e) {         e.printStackTrace();        }       }      });      try {       Thread.sleep(500);      } catch (InterruptedException e) {       e.printStackTrace();      }     }    }   }   ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/105754061 
  • [技术干货] 一次完整的http请求过程
     Http目前协议版本是1.1,HTTP是一种无状态的协议,无状态是指Web浏览器与Web服务器之间,不需要建立持久的链接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(response),链接就被关闭了,在服务器端不保留链接的有关信息。  1.建立TCP链接  在HTTP工作前,首先浏览器与服务器建立链接,该链接是通过TCP完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议方案。一般TCP连接端口号是80。  打开浏览器,地址栏输入blog.csdn.net,开始进行域名解解析:  (1).浏览器自身搜dns缓存  (2).搜索操作系统的dns缓存  (3).读取本地host文件  浏览器发起一个dns系统调用,浏览器获得域名对应的ip地址后,发起一个http三次握手  tcp/ip链接建立起来后,服务器端接受到请求,根据路径参数,经过后端的处理后,把处理后的结果返回给浏览器,浏览器拿到html页面代码,解析和渲染页面,里面的js、css图片资源都需要经过上面的步骤。  2.Web浏览器向服务器发送请求命令  例如:GRT/sample/hello.jsp HTTP/1.1  3.Web浏览器发送请求头信息  浏览器发送请求命令后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送一空白行通知服务器,它已经结束了该头信息的发送。  4.Web服务器应答  应答的第一部分是协议的版本号和应答状态码  如:HTTP/1.1  200OK  5.服务器发送应答头信息(关于服务器自己的数据)  6.服务器向浏览器发送数据  服务器发送头信息后,它会发送一个空白行来表示头信息发送到此为止,接着以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。  7.关闭TCP连接  如果浏览器或服务器在其头信息加入了这行代码,Connection:keep-alive,TCP连接在发送后仍然保持打开状态     HTTP请求信息由3部分组成 1.请求方法URL  协议/版本                  GET/sample.jsp  HTTP/1.1  2.请求头(包含客户端环境、浏览器所用语言、正文长度等)  3.请求正文(包含客户提交的查询字符串信息)  userName=qwe&passWord=123  HTTP响应3个部分构成 1.协议状态版本      HTTP/1.1  200OK  2.响应头(服务器类型、日期时间、内容类型、长度等)     Server:Apache Tomcat/5.0.12   Content-Type:text/html  3.响应正文  <html>  <head>  <title>HTTP响应示例</title>  </head>  </html> 
  • [技术干货] Dubbo支持的协议,8种通信协议之对比
     哪些协议是无状态协议哪些是有状态的,怎么区别? 举个例子我和朋友出去吃饭 不需要每次报上姓名 联系方式 等 朋友就知道我是谁 这是有状态的  而我去办事大厅 工作人员不会记得我是谁 每次去都要填表 出示身份证 这就是无状态的     无状态协议:在下一次链接不记住这一次链接的信息。  HTTP,UDP都是无状态协议  TCP,FTP是有状态协议    无状态服务器是指一种把每个请求作为与之前任何请求都无关的独立的事务的服务器  <<tcp/ip 协议族>>(第二版)第546页有这样一句话:      虽然HTTP使用TCP的服务,但HTTP本身是无状态协议.客户发送请求报文来初始化这个事务.服务器发送响应来回答.  暗示了TCP协议是一个有状态的协议  http协议与tcp、udp区别 http:是用于www浏览的一个协议。 tcp:是机器之间建立连接用的到的一个协议。  TCP协议与UDP协议是传输层协议。 HTTP协议是应用层协议。  Dubbo支持的协议  在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情。你可以根据你应用的创建来选择。例如,使用RMI协议,一般会受到防火墙的限制,所以对于外部与内部进行通信的场景,就不要使用RMI协议,而是基于HTTP协议或者Hessian协议。Dubbo支持8种左右的协议,如下所示:  (1) dubbo:// Dubbo协议 (2) rmi:// RMI协议 (3) hessian:// Hessian协议 (4) http:// HTTP协议 (5) webservice:// WebService协议 (6) thrift:// Thrift协议 (7) memcached:// Memcached协议 (8)redis:// Redis协议 在通信过程中,不同的服务等级一般对应着不同的服务质量,那么选择合适的协议便是一件非常重要的事情。你可以根据你应用的创建来选择。 例如,使用RMI协议,一般会受到防火墙的限制,所以对于外部与内部进行通信的场景,就不要使用RMI协议,而是基于HTTP协议或者Hessian协议。  部分协议的特点和使用场景如下:  1、dubbo协议  Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。  缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。 连接个数:单连接 连接方式:长连接 传输协议:TCP 传输方式:NIO异步传输 序列化:Hessian二进制序列化 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。 适用场景:常规远程服务方法调用 为什么要消费者比提供者个数多: 因dubbo协议采用单一长连接, 假设网络为千兆网卡(1024Mbit=128MByte), 根据测试经验数据每条连接最多只能压满7MByte(不同的环境可能不一样,供参考), 理论上1个服务提供者需要20个服务消费者才能压满网卡。   为什么不能传大包: 因dubbo协议采用单一长连接, 如果每次请求的数据包大小为500KByte,假设网络为千兆网卡(1024Mbit=128MByte),每条连接最大7MByte(不同的环境可能不一样,供参考), 单个服务提供者的TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。 单个消费者调用单个服务提供者的TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。 如果能接受,可以考虑使用,否则网络将成为瓶颈。   为什么采用异步单一长连接: 因为服务的现状大都是服务提供者少,通常只有几台机器, 而服务的消费者多,可能整个网站都在访问该服务, 比如Morgan的提供者只有6台提供者,却有上百台消费者,每天有1.5亿次调用, 如果采用常规的hessian服务,服务提供者很容易就被压跨, 通过单一连接,保证单一消费者不会压死提供者, 长连接,减少连接握手验证等, 并使用异步IO,复用线程池,防止C10K问题。  2、RMI  RMI协议采用JDK标准的java.rmi.*实现,采用阻塞式短连接和JDK标准序列化方式  Java标准的远程调用协议。 连接个数:多连接 连接方式:短连接 传输协议:TCP 传输方式:同步传输 序列化:Java标准二进制序列化 适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。 适用场景:常规远程服务方法调用,与原生RMI服务互操作  3、hessian  Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现  基于Hessian的远程调用协议。   连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:Hessian二进制序列化 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。 适用场景:页面传输,文件传输,或与原生hessian服务互操作    4、http  采用Spring的HttpInvoker实现   基于http表单的远程调用协议。   连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:表单序列化(JSON) 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。 适用场景:需同时给应用程序和浏览器JS使用的服务。    5、webservice  基于CXF的frontend-simple和transports-http实现   基于WebService的远程调用协议。   连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:SOAP文本序列化 适用场景:系统集成,跨语言调用。    6、thrif  Thrift是Facebook捐给Apache的一个RPC框架,当前 dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如service name,magic number等。 ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/107820609 
  • [技术干货] java中关于深拷贝的几种方式
     在java里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝。  浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化。 深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。 方式1:构造函数深拷贝 package com.lyj.demo.pojo.cloneTest; import lombok.Getter; /**  * @author 凌兮  * @date 2021/4/15 14:28  * 通过构造器进行深拷贝测试  */ @Getter public class UserConstruct {     private String userName;     private AddressConstruct address;     public UserConstruct() {     }     public UserConstruct(String userName, AddressConstruct address) {         this.userName = userName;         this.address = address;     }     public static void main(String[] args) {         AddressConstruct address = new AddressConstruct("小区1", "小区2");         UserConstruct user = new UserConstruct("小李", address);         // 调用构造函数进行深拷贝         UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2()));         // 修改源对象的值         user.getAddress().setAddress1("小区3");         // false         System.out.println(user == copyUser);         // false         System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1());         // false         System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));         // true         System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2()));     } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /**  * @author 凌兮  * @date 2021/4/15 14:28  */ @Getter @Setter public class AddressConstruct {     private String address1;     private String address2;     public AddressConstruct() {     }     public AddressConstruct(String address1, String address2) {         this.address1 = address1;         this.address2 = address2;     } }  方式2:重载Clone()方法深拷贝 Object父类有个clone()的拷贝方法,不过它是protected类型的 ,我们需要重写它并修改为public类型,除此之外,子类还需要实现Cloneable接口来告诉JVM这个类上是可以拷贝的。  package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /**  * @author 凌兮  * @date 2021/4/15 14:49  *  */ @Setter @Getter public class AddressClone implements Cloneable{     private String address1;     private String address2;     public AddressClone() {     }     public AddressClone(String address1, String address2) {         this.address1 = address1;         this.address2 = address2;     }     @Override     protected AddressClone clone() throws CloneNotSupportedException {         return (AddressClone) super.clone();     } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; /**  * @author 凌兮  * @date 2021/4/15 14:48  * 通过实现Clone接口实现深拷贝  */ @Setter @Getter public class UserClone implements Cloneable{     private String userName;     private AddressClone address;     public UserClone() {     }     public UserClone(String userName, AddressClone address) {         this.userName = userName;         this.address = address;     }     /**      * Object父类有个clone()的拷贝方法,不过它是protected类型的,      * 我们需要重写它并修改为public类型。除此之外,      * 子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。      * @return      * @throws CloneNotSupportedException      */     @Override     protected UserClone clone() throws CloneNotSupportedException {         // 需要注意的是,super.clone()其实是浅拷贝,         // 所以在重写UserClone类的clone()方法时,address对象需要调用address.clone()重新赋值         UserClone userClone = (UserClone) super.clone();         userClone.setAddress(this.address.clone());         return userClone;     }     public static void main(String[] args) throws CloneNotSupportedException {         AddressClone address = new AddressClone("小区1", "小区2");         UserClone user = new UserClone("小李", address);         UserClone copyUser = user.clone();         user.getAddress().setAddress1("小区3");         // false         System.out.println(user == copyUser);         // false         System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));     } }  需要注意的是,super.clone()其实是浅拷贝,所以在重写User类的clone()方法时,address对象需要调用address.clone()重新赋值。  方式3:Apache Commons Lang序列化方式深拷贝 Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。  Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。  package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; import java.io.Serializable; /**  * @author 凌兮  * @date 2021/4/15 15:11  */ @Getter @Setter public class AddressSerializable implements Serializable {     private String address1;     private String address2;     public AddressSerializable() {     }     public AddressSerializable(String address1, String address2) {         this.address1 = address1;         this.address2 = address2;     } } package com.lyj.demo.pojo.cloneTest; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.SerializationUtils; import java.io.Serializable; /**  * @author 凌兮  * @date 2021/4/15 15:10  * 通过Apache Commons Lang 序列化方式深拷贝  * Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。  * 但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。  * Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。  */ @Getter @Setter public class UserSerializable implements Serializable {     private String userName;     private AddressSerializable address;     public UserSerializable() {     }     public UserSerializable(String userName, AddressSerializable address) {         this.userName = userName;         this.address = address;     }     public static void main(String[] args) {         AddressSerializable address = new AddressSerializable("小区1", "小区2");         UserSerializable user = new UserSerializable("小李", address);         UserSerializable copyUser = SerializationUtils.clone(user);         user.getAddress().setAddress1("小区3");         // false         System.out.println(user == copyUser);         // false         System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));     } } ———————————————— 版权声明:本文为CSDN博主「wsb8233696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsb8233696/article/details/130907780 
  • [技术干货] Java中双冒号: :的作用
     一、定义 双冒号运算操作符是类方法的句柄,lambda表达式的一种简写  表达式: person -> person.getName(); 可以替换成: Person::getName   表达式: () -> new HashMap<>(); 可以替换成: HashMap::new 二、如何理解 双冒号(::)运算符在Java 8中被用作方法引用(method reference),方法引用是与lambda表达式相关的一个重要特性。它提供了一种执行方法的方法,为此,方法引用需要由兼容的函数式接口组成的目标类型上下文。使用lambda表达式会创建匿名函数, 但有时候需要使用一个lambda表达式只调用一个已经存在的方法(不做其它), 所以这才有了方法引用!  其实,JVM 本身并不支持指向方法引用,过去不支持,现在也不支持。Java 8 对方法引用的支持只是编译器层面的支持,虚拟机执行引擎并不了解方法引用。编译器遇到方法引用的时候,会像上面那样自动推断出开发者的意图,将方法引用还原成接口实现对象,或者更形象地说,就是把方法引用设法包装成一个接口实现对象,这样虚拟机就可以无差别地执行字节码文件而不需要管什么是方法引用了。  需要注意的是,方法引用是用来简化接口实现代码的,并且凡是能够用方法引用来简化的接口,都有这样的特征:有且只有一个待实现的方法。这种接口在 Java 中有个专门的名称: 函数式接口。当试图用方法引用替代一个非函数式接口时,会有这样的错误提示: xxx is not a functional interface。 三、使用场景 类型  引用语法  案例  引用静态方法  类名::静态方法名  Integer::parseInt  引用特定对象实例方法  对象::实例方法名  System.out::println  引用特定类型的任意对象的实例方法  特定类型::实例方法名  String::compareToIgnoreCase  引用超类(父类)实例方法  super::方法名  引用类构造方法  类名::new  ArrayList::new  引用数组构造方法  数组类型[]::new  String[]::new  案例详解:  引用静态方法  import org.junit.Test; import java.util.Arrays; import java.util.List;   public class Colon{ @Test     public void test(){         List<String> list = Arrays.asList("a", "b","c");         //静态方法引用ClassName::methodName         list.forEach(Colon:: print);         //上一行等价于         //1ist.forEach((x)->Colon.print(x));     }     //静态方法     public static void print(String s){         system.out.println(s);     } }  引用特定对象实例方法  import org.junit.Test; import java.util.Arrays; import java.util.List; public class Colon{     @Test     public void test(){         List<string> list = Arrays.asList("a", "b","c");//r实例方法引用instanceRef: :methodName         list.forEach(new Colon()::print);         //上一行等价于         //iist.forEach((x)->new Colon().print(x));     }     //实例方法     public void print(String s){         System.out.println(s);     } }  引用特定类型的任意对象的实例方法  import org.junit.Test; import java.util.Arrays;   public class Colon{     @Test     public void test(){         String[] arr = { "Barbara","James","Mary", "John",                 "Patricia","Robert","Michae1", "Linda”};         //引用String类型的任意对象的compareToIgnoreCase方法实现忽略大小写排序         Arrays.sort(arr, String::compareToIgnoreCase);         //上一行等价于         //Arrays.sort(arr, (a,b)->a.compareToIgnoreCase(b));         //输出         for(String s:arr){             System.out.println(s);         } }  引用超类(父类)实例方法  import org.junit.Test; import java.util.Arrays; import java.util.List;   public class Colon extends BaseColon{     @Test     public void test(){         List<string> list = Arrays.asList("a", "b","c");         //实例方法引用instanceRef::methodName         list.forEach(super:: print);     } } class Basecolon{     //实例方法     public void print(string s){         System.out.println(s);     } }  引用类构造方法  一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。  //注意:该类无需实现接口 public class Colon{     private String name;     private int age;     //无参构造     public Colon(){ }     //有参构造     public colon(String name, int age){         this.name = name;         this.age = age;     }     public static void main(String[] args){         //无参构造引用         ColonNoParam cnp = Colon::new;         colon c1 = cnp.createColon();         System.out.println(c1);         //有参构造引用         ColonlithParam cwp = Colon::new;         colon c2 = cwp.createColon("小明",20);         System.out.println(c2);     }     //生成toString方法打印查看     @Override     public string toString() {         return "Colon{" +                 "name='"+ name + "\"+                 ",age=" + age +                 '}';     } } interface colonNoParam{     //无参方法提供对象     Colon createColon(); } interface ColonwithParam{     //有参方法提供对象(数据类型要与colon有参构造函数对应)     colon createColon(String s,int i); }  引用数组构造方法  我们可以借助jdk自带的java.util.function.Function类实现对数组构造函数的引用。  当然,我们也可以使用@FunctionalInterface自定义函数式接口来实现:  public class Colon{     public static void main(string[] args) {         MyArrayFunction<Integer,Colon[]> function = Colon[]::new;         //调用apply方法创建数组,这里的5是教组的长度         colon[] arr = function.apply(5);         //循环输出-初始都为null         for(Colon c:arr){             System.out.println(c);         }     } } //自定义函教式接口 @FunctionalInterface interface MyArrayFunction<A,T>{     T apply(A a); }  参考博客:https://blog.csdn.net/yangzhe19931117/article/details/128246653 ———————————————— 版权声明:本文为CSDN博主「橙橙爱学习」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_52293201/article/details/129433338 
  • [技术干货] JAVA——二维数组遍历二维数组的三种方法
     🍒java中二维数组的定义和赋值  int[][] array = new int[3][];   //java行不能省略,列可以省略    array[0] = new int[]{0, 1, 2};//赋值  array[2] = new int[]{4, 5, 6};           int[][] array1={{1,1},{2,2},{3,3}};//定义并且初始化 🍒二维数组遍历的三种方法 二维数组其实就是特殊的一维数组;  在java中将这句话诠释得淋漓尽致;  🍇第一种:for循环遍历        int[][] array = new int[3][];   //java行不能省略,列可以省略         array[0] = new int[]{0, 1, 2};         array[2] = new int[]{4, 5, 6,7};            for(int i=0;i< array.length;i++){             if(array[i]==null) {                 System.out.println("null"+" ");                 continue;             }//当二维数组某一行为空时直接跳过循环遍历下一行             for (int j = 0; j < array[i].length; j++) {                 System.out.print(array[i][j]+" ");             }//二维数组的每一行元素都相当于一个一维数组,              //遍历一维数组,长度就是array[i].length             System.out.println();            }       运行截图:  🍇第二种方法:通过Arrays.deepToString()遍历        int[][] array = new int[3][];   //java行不能省略,列可以省略         array[0] = new int[]{0, 1, 2};         array[2] = new int[]{4, 5, 6,7};         System.out.println(Arrays.deepToString(array)); 运行截图:  🍇第三种方法:通过for(   :   )遍历          int[][] array = new int[3][];   //java行不能省略,列可以省略         array[0] = new int[]{0, 1, 2};         array[2] = new int[]{4, 5, 6,7};             int i=0;            for(int[] ret:array){                if(array[i]==null) {                    System.out.println("null"+" ");                    i++;                    continue;                }                for(int x:ret){                    System.out.print(x+" ");                }                System.out.println();              i++; 注意:冒号左边填写的是数组每个元数的类型,右边填写的是数组名 ———————————————— 版权声明:本文为CSDN博主「熬夜退役选手337」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_61638178/article/details/131697975 
  • [技术干货] 类是如何加载的
    在 Java 中,类加载的流程有一个专门的机制叫做“类加载机制”。类加载机制是指一个类在 Java 虚拟机(JVM)中的执行流程,它也是 Java 程序能够正常执行的关键所在,那它的具体执行流程是啥?接下来我们一起来看。流程概述在 JVM 中,类加载会经历以下 5 个阶段:加载阶段(Loading)验证阶段(Verification)准备阶段(Preparation)解析阶段(Resolution)初始化阶段(Initialization)其中:验证阶段、准备阶段和解析阶段合起来又称为连接阶段,所以以上 5 个阶段又可以划分为 3 大类:加载阶段(Loading)连接阶段(Linking)验证阶段(Verification)准备阶段(Preparation)解析阶段(Resolution)初始化阶段(Initialization)这 3 大类、5 个流程的具体执行细节是这样的。1.加载阶段简单来说,加载阶段就是将类文件加载到内存中的过程。在加载阶段,JVM 需要完成以下 3 件事:通过一个类的全限定名来获取定义此类的二进制字节流;将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。2.连接阶段连接阶段又分为:验证阶段(Verification)、准备阶段(Preparation)和解析阶段(Resolution),具体执行的细节如下。2.1 验证阶段验证阶段也叫做校验阶段,它主要是用来验证加载到内存中的类是否是安全合规的文件,验证的主要动作大概有以下几个(当然,以下细节如果实在记不住也没关系):文件格式校验包括常量池中的常量类型、Class 文件的各个部分是否被删除或被追加了其他信息等;元数据校验包括父类正确性校验(检查父类是否有被 final 修饰)、抽象类校验等;字节码校验,此步骤最为关键和复杂,主要用于校验程序中的语义是否合法且符合逻辑;符号引用校验,对类自身以外比如常量池中的各种符号引用的信息进行匹配性校验。2.2 准备阶段准备阶段就开始给类中的静态变量设置默认值了,注意这里不是给静态变量设置初始值,而是设置默认值,二者还是有很大区别的。举个例子,比如代码中写的内容是:public static int number = 10;那么此时是给 number 变量设置的 int 值是默认值 0,而非初始值 10。2.3 解析阶段解析阶段就是将常量池中的符号引用更换成直接引用了,所谓的符号引用是指以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可;而直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。符号引用和直接引用有一个重要的区别:使用符号引用时被引用的目标不一定已经加载到内存中;而使用直接引用时,引用的目标必定已经存在虚拟机的内存中了。3.初始化阶段初始化阶段,Java 虚拟机真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。到这一步骤之后,类的加载过程就算正式完成了,此时会给静态变量设置初始值,并执行静态代码块的内容。总结类加载流程总共分为 3 大类,5 个主要流程:加载阶段(Loading):将类文件加载到内存。连接阶段(Linking)验证阶段(Verification):类文件安全性效验。准备阶段(Preparation):给静态变量设置默认值。解析阶段(Resolution):将符号引用转换为直接引用。初始化阶段(Initialization):执行静态代码块和给静态变量设置初始值。转载自https://www.cnblogs.com/vipstone/p/17069838.html
总条数:692 到第
上滑加载中