微服务开发:断路器详解
微服务是目前业界使用的最重要的实现方面。通过使用微服务架构,开发人员可以消除他们以前在单体应用程序中遇到的许多问题。展望未来,人们开始在微服务中搜索和采用各种模式。大多数时候,新模式的产生是为了解决另一个模式中出现的常见问题。就这样,随着时间的推移,大量的模式进入了实践。您可以从这里获得完整的摘要: https://microservices.io/patterns/microservices.html
考虑到它们的范围,这些微服务模式进一步分为几类。在所有这些模式中,许多开发人员都使用了一些非常重要和流行的模式。断路器是其中之一,有助于以适当的方式管理下游服务故障。让我们了解这种模式的作用。💪
一、断路器介绍
1.1 什么是断路器模式?
您可能已经听说过我们在电子产品中发现的断路器。它的主要目的是什么?简单地说,在意想不到的情况下切断电流。与此相同,这种微服务模式也因其具有相同的性质而得名。
这种模式在服务之间进行通信时出现。让我们来看一个简单的场景。假设我们有两个服务:服务 A 和 B。服务 A 正在调用服务 B(API 调用)以获取所需的一些信息。当服务 A 调用服务 B 时,如果服务 B 由于某些基础设施中断而关闭,会发生什么?服务 A 没有得到结果,它将因抛出异常而挂起。然后另一个请求来了,它也面临同样的情况。就像这个请求线程将被阻塞/挂起,直到服务 B 出现!结果,网络资源将被耗尽,性能低下,用户体验差。级联故障也可能因此发生。
在这种情况下,我们可以使用这种断路器模式来解决问题。它为我们提供了一种在不打扰最终用户或应用程序资源的情况下处理这种情况的方法。
1.2 模式如何运作?💥
基本上,它的行为与电路断路器相同。当应用程序的远程服务调用失败次数超过给定阈值时,断路器将在特定时间段内跳闸。在此超时到期后,断路器允许有限数量的请求通过它。如果这些请求成功,则断路器将关闭并恢复正常操作。否则,如果它们失败了,超时时间将重新开始,然后像以前一样做剩下的事情。
以下是一些常见概念讲解帮助大家来理解断路器。😎
1.3 模式状态的生命周期💥
断路器模式中讨论了 3 个主要状态。他们是:
-
CLOSED
-
OPEN
-
HALF OPEN
让我们简要了解一下状态……
CLOSED State
当正在交互的两个服务都启动并运行时,断路器默认关闭。断路器会持续统计远程 API 调用的次数。
OPEN State
一旦远程 API 调用失败百分比超过给定阈值,断路器就会将其状态更改为 OPEN 状态。调用微服务会立即失败,返回异常。也就是说,流量中断了。
HALF OPEN State
在 OPEN 状态停留给定的超时时间后,断路器自动将其状态变为 HALF OPEN 状态。在这种状态下,只允许有限数量的远程 API 调用通过。如果失败调用计数大于此有限数量,则断路器再次变为 OPEN 状态,流量继续中断。否则关闭断路器,流量恢复正常。
Pattern states
为了实际演示该模式,我将使用 Spring Boot 框架来创建微服务。并用 Resilience4j 库实现断路器。
1.4 什么 Resilience4j?
Resilience4j 是一个轻量级、易于使用的容错库,其灵感来自于 Netflix Hystrix。它提供各种功能如下:
-
断路器 — 容错
-
速率限制器 — 阻止太多请求
-
时间限制器 — 调用远程操作时的限制时间
-
重试机制 — 失败操作自动重试
-
隔板 — 限制并发请求数
-
缓存 — 存储远程操作的结果
在本文中,我们将基于 Spring Boot 项目来使用第一个功能。😎
二、代码讲解
2.1 创建 2️⃣ 个微服务
我将使用名为 loan-service(贷款) 和 rate-service(利率) 的两个服务来实现一个简单的服务间通信场景。
技术细节:
带有 H2 内存中 DB、JPA、Hibernate、Actuator、Resilience4j 的 Spring Boot
脚本:
贷款服务可以获取保存在数据库中的贷款,每个贷款对象都有贷款类型。根据贷款类型,有单独的利率百分比。因此,利率服务的名称包含那些利率对象的详细信息。
-
我将从贷款服务调用利率服务,请求给定贷款类型的利率。
-
然后我必须根据贷款类型计算贷款的总利息价值。
-
然后我将使用从利率服务获得的利率更新所有贷款对象的利息金额。
Project setup
由于费率服务是独立的,我将首先实现费率服务的基本功能。
使用 POM 文件下方提供的依赖项创建一个新的 Spring Boot 项目。我将其命名为费率服务。https://github.com/SalithaUCSC/spring-boot-circuit-breaker/blob/main/rate-service/pom.xml
Controller:
@RestController
@RequestMapping("api")
public class RateController {@Autowiredprivate RateService rateService;@GetMapping(path = "/rates/{type}")public ResponseEntity<Rate> getRateByType(@PathVariable("type") String type) {return ResponseEntity.ok().body(rateService.getRateByType(type));}
}
Service:
@Service
public class RateService {@Autowiredprivate RateRepository repository;public Rate getRateByType(String type) {return repository.findByType(type).orElseThrow(() -> new RuntimeException("Rate Not Found: " + type));}
}
Repository:
@Repository
public interface RateRepository extends JpaRepository<Rate, Integer> {Optional<Rate> findByType(String type);
}
Entity:
@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "rates")
public class Rate {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)Integer id;String type;@Column(name = "rate")Double rateValue;
}
Configuration:
server:port: 9000
spring:application:name: rate-servicedatasource:url: jdbc:h2:mem:cb-rate-dbusername: rootpassword: 123driverClassName: org.h2.Driverjpa:database-platform: org.hibernate.dialect.H2Dialecthibernate:ddl-auto: create-droph2:console:enabled: true
启动类:主类将在服务即将到来时添加两种类型的贷款利率。
@SpringBootApplication
public class RateServiceApplication {@Autowiredprivate RateRepository rateRepository;public static void main(String[] args) {SpringApplication.run(RateServiceApplication.class, args);}@PostConstructpublic void setupData() {rateRepository.saveAll(Arrays.asList(Rate.builder().id(1).typ并检查我们需要的 APIe("PERSONAL").rateValue(10.0).build(),Rate.builder().id(2).type("HOUSING").rateValue(8.0).build()));}
}
现在我们可以启动 rate-service 并检查我们需要的 API。转到 http://localhost:9000/api/rates/PERSONAL 并查看结果。你应该得到这个回应。
{"id": 1,"type": "PERSONAL","rateValue": 10}
2.2 贷款服务添加断路器
现在我需要实施贷款服务。 loan-service 内部需要断路器,因为它正在调用 rate-service。因此,需要 Resilience4j 库。我需要检查断路器的状态。为此,我需要在贷款服务中启用 Actuator。
使用 POM 文件下方提供的依赖项创建一个新的 Spring Boot 项目。我将其命名为贷款服务。 https://github.com/SalithaUCSC/spring-boot-circuit-breaker/blob/main/loan-service/pom.xml
让我们为贷款服务添加基本功能。
Controller:
@RestController
@RequestMapping("api")
public class LoanController {@Autowiredprivate LoanService loanService;@GetMapping(path = "/loans")public ResponseEntity<List<Loan>> getLoansByType(@RequestParam("type") String type) {return ResponseEntity.ok().body(loanService.getAllLoansByType(type.toUpperCase()));}}
Repository:
public interface LoanRepository extends JpaRepository<Loan, Integer> {List<Loan> findByType(String type);
}
DTO:这用于转换来自费率服务 API 调用的响应。因为它是 Rate 的类型。同rate-service Rate实体类(只是省略了ORM相关的东西)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class InterestRate {Integer id;String type;Double rateValue;
}
Entity:
@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "loans")
public class Loan {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)Integer id;String type;Double amount;Double interest;
}
启动类:
-
主类将在服务即将到来时添加 3 个贷款对象。利息金额已设置为零,因为我们后来通过远程调用 rate-service 对其进行了更新。
-
我们需要一个 RestTemplate 类的 Bean 来执行远程 API 调用。如果您不知道,请从此处阅读: https://salithachathuranga94.medium.com/rest-template-with-spring-boot-e2001a8219e6
@SpringBootApplication
public class LoanServiceApplication {@Autowiredprivate LoanRepository loanRepository;public static void main(String[] args) {SpringApplication.run(LoanServiceApplication.class, args);}@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}@PostConstructpublic void setupData() {loanRepository.saveAll(Arrays.asList(Loan.builder().id(1).type("PERSONAL").amount(200000.0).interest(0.0).build(),Loan.builder().id(2).type("HOUSING").amount(6000000.0).interest(0.0).build(),Loan.builder().id(3).type("PERSONAL").amount(100000.0).interest(0.0).build()));}
}
Service:
这是我们执行远程调用的最重要的地方。我们需要在利率服务中使用 RestTemplate: http://localhost:9000/api/rates/{type} 调用此 API 以获取贷款类型的百分比。然后我们计算利息金额为贷款金额*(利率/100)并更新贷款利息金额。
@Service
public class LoanService {@Autowiredprivate LoanRepository loanRepository;@Autowiredprivate RestTemplate restTemplate;private static final String SERVICE_NAME = "loan-service";private static final String RATE_SERVICE_URL = "http://localhost:9000/api/rates/";public List<Loan> getAllLoansByType(String type) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntity<InterestRate> entity = new HttpEntity<>(null, headers);ResponseEntity<InterestRate> response = restTemplate.exchange((RATE_SERVICE_URL + type),HttpMethod.GET, entity,InterestRate.class);InterestRate rate = response.getBody();List<Loan> loanList = new ArrayList<>();if (rate != null) {loanList = loanRepository.findByType(type);for (Loan loan : loanList) {loan.setInterest(loan.getAmount() * (rate.getRateValue() / 100));}}return loanList;}
}
Configuration:
server:port: 8000
spring:application:name: loan-servicedatasource:url: jdbc:h2:mem:cb-loan-dbusername: rootpassword: 123driverClassName: org.h2.Driverjpa:database-platform: org.hibernate.dialect.H2Dialecthibernate:ddl-auto: create-droph2:console:enabled: true
management:endpoint:health:show-details: alwaysendpoints:web:exposure:include: healthhealth:circuitbreakers:enabled: true
我们需要在执行器暴漏断路器详细信息(暴漏端点)。稍后我们将在此处添加断路器配置。目前,不需要。
现在我们可以启动 rate-service 并检查我们需要的 API。转到 http://localhost:8000/api/loans?type=personal 并查看结果。你应该得到这个回应。
[
{"id": 1,"type": "PERSONAL","amount": 200000,"interest": 20000},
{"id": 3,"type": "PERSONAL","amount": 100000,"interest": 10000}
]
2.3 添加回退方法💥
现在我们必须用注释来丰富我们的 Loan 服务方法。它被称为 “@CircuitBreaker”。在这里,SERVICE_NAME 被视为“贷款服务”。然后我们必须提供一个 fallbackMethod。这样做的目的是当下游服务(速率服务)无法响应时默认调用它。
@CircuitBreaker(name = SERVICE_NAME, fallbackMethod = "getDefaultLoans")
public List<Loan> getAllLoansByType(String type) {...............
}
我已经设置了当费率服务没有响应时默认返回空列表的方法。
您可以设置此方法以显示错误消息,而不发送空消息。你可以返回这样的东西 — “Rate service is not responding.请求失败!”。发送空数组或一组默认数据不是理想的方式。因为这会给用户带来困惑。但是您必须确保这两种方法都返回相同类型的数据。在我的例子中:两种方法都返回列表!
public List<Loan> getDefaultLoans(Exception e) {return new ArrayList<>();
}
2.4 添加断路器配置💥
让我们添加 Resilience4j 断路器配置。将此添加到贷款服务中的 application.yml。
resilience4j:circuitbreaker:instances:loan-service:registerHealthIndicator: truefailureRateThreshold: 50minimumNumberOfCalls: 5automaticTransitionFromOpenToHalfOpenEnabled: truewaitDurationInOpenState: 5spermittedNumberOfCallsInHalfOpenState: 3slidingWindowSize: 10slidingWindowType: COUNT_BASED
-
failureRateThreshold — 失败阈值的预期百分比。我将其设置为 50%。这意味着,当失败的远程调用总数 % 等于或大于 50% 时,断路器将处于活动状态以停止进一步的请求。
-
minimumNumberOfCalls — 决定启用断路器的失败百分比的 API 调用总数的最小值。我将其设置为 5。假设前 5 个 API 调用中有 3 个 API 调用失败。这意味着 failureRateThreshold = (3/5) * 100 = 60%。
-
automaticTransitionFromOpenToHalfOpenEnabled — 我已将其设置为 true。当转换的正确时间到来时,它会自动将 OPEN 状态转换为 HALF OPEN 状态
-
waitDurationInOpenState — 从 OPEN 状态进入 HALF OPEN 状态之前的超时时间。 5 秒后,断路器就将更改状态。
-
permittedNumberOfCallsInHalfOpenState — 在 HALF OPEN 状态下应发送的 LIMITED API 调用数。我将其设置为 3。因此,在 3 次 API 调用之后,如果失败,则断路器将再次进入 OPEN 状态。否则断路器将关闭,因为 rate-service 已启动。
-
slidingWindowType:我在这里设置了类型以根据请求计数保持断路器行为。
启动这两个服务。现在转到贷款服务,输入监控端点 URL:http://localhost:8000/actuator/health。断路器的详细信息会在响应中突出显示。
{"status": "UP","components": {"circuitBreakers": {"status": "UP","details": {"loan-service": {"status": "UP","details": {"failureRate": "-1.0%","failureRateThreshold": "50.0%","slowCallRate": "-1.0%","slowCallRateThreshold": "100.0%","bufferedCalls": 1,"slowCalls": 0,"slowFailedCalls": 0,"failedCalls": 0,"notPermittedCalls": 0,"state": "CLOSED"}}}},......................................}
}
-
bufferedCalls — 从贷款服务到利率服务的总 API 调用
-
failedCalls - 从贷款服务到利率服务的失败 API 调用总数
-
failureRate — (failedCalls/bufferedCalls) * 100%
2.5 测试断路器💥
我们必须遵循一些有序的步骤才能准确地看到变化。在每一步中,我们都必须查看监控端点,并通过更改其状态查看断路器的行为方式。开始!💪
-
启动两个微服务。贷款服务在 8000 上运行,利率服务在 9000 上运行。
-
现在点击此 API 2 次:http://localhost:8000/api/loans?type=personal。然后去检查监控端点 http://localhost:8000/actuator/health。现在 bufferedCalls 计数已按预期更新为 2。由于费率服务已启动,断路器仍处于关闭状态。
{"loan-service": {"status": "UP","details": {"failureRate": "-1.0%","failureRateThreshold": "50.0%","slowCallRate": "-1.0%","slowCallRateThreshold": "100.0%","bufferedCalls": 2,"slowCalls": 0,"slowFailedCalls": 0,"failedCalls": 0,"notPermittedCalls": 0,"state": "CLOSED"}}
}
-
现在停止费率服务!然后点击贷款服务 API URL 3次:http://localhost:8000/api/loans?type=personal。你应该得到一个我们设置为后备的空数组!这将使 bufferedCalls 计数为 5(前 2 个和这个 3)。同时,failedCalls 计数更新为 3。现在 failureRate 变为 60%( (3/5) * 100% )。然后它已经超过了我们的阈值:50%。😀然后断路器将其状态更改为 OPEN!😍
{"loan-service": {"status": "CIRCUIT_OPEN","details": {"failureRate": "60.0%","failureRateThreshold": "50.0%","slowCallRate": "0.0%","slowCallRateThreshold": "100.0%","bufferedCalls": 5,"slowCalls": 0,"slowFailedCalls": 0,"failedCalls": 3,"notPermittedCalls": 0,"state": "OPEN"}}
}
-
然后等待 5 秒钟。它应该在 5 秒后转换为半开状态,根据我们的配置,我们将 waitDurationInOpenState 设置为 5s 这是超时时间。在这段时间之后,请求计数也将被重置。
{"loan-service": {"status": "CIRCUIT_HALF_OPEN","details": {"failureRate": "-1.0%","failureRateThreshold": "50.0%","slowCallRate": "-1.0%","slowCallRateThreshold": "100.0%","bufferedCalls": 0,"slowCalls": 0,"slowFailedCalls": 0,"failedCalls": 0,"notPermittedCalls": 0,"state": "HALF_OPEN"}}
}
-
在 HALF OPEN 状态下,有限数量的请求将被允许通过。在我们的例子中,它在配置中为 3,相关值已设置为 permittedNumberOfCallsInHalfOpenState: 3。
由于 rate-service 仍然关闭,只需再次尝试 loan-service API 3次!http://localhost:8000/api/loans?type=personal 发生了什么事? 3次调用全部失败!那么 failureRate 就是 100%。我们的断路器将再次打开。
{"loan-service": {"status": "CIRCUIT_OPEN","details": {"failureRate": "100.0%","failureRateThreshold": "50.0%","slowCallRate": "0.0%","slowCallRateThreshold": "100.0%","bufferedCalls": 3,"slowCalls": 0,"slowFailedCalls": 0,"failedCalls": 3,"notPermittedCalls": 0,"state": "OPEN"}}
}
-
超时 5 秒后,它将再次变为半开状态!使用执行器再次检查。你应该得到一个用于贷款服务 API 调用的空数组...
-
现在开始收费服务!😎然后再次尝试此 API 3次:http://localhost:8000/api/loans?type=personal 发现断路器已关闭!😍因为成功执行了预期的有限 API 调用计数。
{"loan-service": {"status": "UP","details": {"failureRate": "-1.0%","failureRateThreshold": "50.0%","slowCallRate": "-1.0%","slowCallRateThreshold": "100.0%","bufferedCalls": 0,"slowCalls": 0,"slowFailedCalls": 0,"failedCalls": 0,"notPermittedCalls": 0,"state": "CLOSED"}}
}
自此,我们完成了断路器的测试工作。是不是很神奇?😎它正在按预期工作!
完整的源代码可以在这个 GitHub 存储库中找到:https://github.com/SalithaUCSC/spring-boot-circuit-breaker
最后感谢大家阅读,希望这篇文章能为你提供价值。公众号【waynblog】分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力😘。
相关文章:
微服务开发:断路器详解
微服务是目前业界使用的最重要的实现方面。通过使用微服务架构,开发人员可以消除他们以前在单体应用程序中遇到的许多问题。展望未来,人们开始在微服务中搜索和采用各种模式。大多数时候,新模式的产生是为了解决另一个模式中出现的常见问题。…...
Leetcode—383.赎金信【简单】
2023每日刷题(五十) Leetcode—383.赎金信 实现代码 class Solution { public:int arr[26] {0};int arr2[26] {0};bool canConstruct(string ransomNote, string magazine) {int len ransomNote.size();int len2 magazine.size();for(int i 0; i …...
爬虫-xpath篇
1.xpath的基础语法 表达式描述nodename选中该元素/从根节点选取、或者是元素和元素间的过渡//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置.选取当前节点…选取当前节点的父节点选取属性text()选取文本 举例: 路径表达式结果html选择html元…...
Oracle初始化参数文件pfile和spfile
pfile :Oracle 9i之前,ORACLE一直采用PFILE方式存储初始化参数,该文件为文本文件,可以在操作系统级别修改。当spfile文件修改出现错误导致oracle无法启动时,可以使用 pfile文件启动数据库 spfile:从Oracle…...
zookeeper 客户端常用命令简单记录(实操课程系列--watcher功能测试)(发布订阅功能测试)
本系列是zookeeper相关的实操课程,课程测试环环相扣,请按照顺序阅读测试来学习zookeeper。阅读本文之前,请先阅读----zookeeper 单机伪集群搭建简单记录(实操课程系列) 1、命令行工具切换到zookeeper的bin目录下面&am…...
Scrapy爬虫数据存储为JSON文件的解决方案
什么是JSON文件 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人们阅读和编写,同时也易于机器解析和生成。它基于JavaScript Spark语言的一个子集,但独立于Smashing语言,因此在许多中…...
计算机设计大赛 选题推荐
0 前言 比赛介绍 中国大学生计算机设计大赛是中国高等教育学会“全国普通高校大学生竞赛排行榜”竞赛之一。自2008年开赛至2019年,一直由教育部高校与计算机相关教指委等或独立或联合主办。2023年(第16届)中国大学生计算机设计大赛是由、中…...
基于springboot,vue高校图书馆管理系统
开发工具:IDEA 服务器:Tomcat9.0, jdk1.8 项目构建:gradle-5.6.4 数据库:mysql5.7 系统分前后台,项目采用前后端分离 前端技术:vueelementUI 服务端技术:springbootmybatisred…...
如何打造更高效、安全、灵活的企业网络组网方案
随着互联网的快速发展,企业对于网络的需求也变得越来越高。然而,企业规模不断扩大,分布式办公越来越普遍,如何保证数据安全传输和网络稳定运行是每一家企业都需要面对的问题。因此,合理构建企业组网架构已经成为了现代…...
MATLAB Simulink +STM32硬件在环 (HIL)实现例程测试
MATLAB Simulink STM32硬件在环 (HIL)实现例程测试 📍相关篇《STM32CubeMxMATLAB Simulink点灯程序》✨本例程没有使用到STM32CubeMX来创建工程(在Simulink 中不是选择的STM32xxxbased类型的)。 🔖STM32xxx…...
Kubernetes Service控制器详解以及切换为ipvs代理模式
文章目录 一、Service 存在的意义二、Pod与Service的关系三、Service定义与创建四、Service三种常用类型五、Service代理模式六、切换Service代理模式七、service总体工作流程八、kube-proxy ipvs和iptables的异同九、Service DNS名称 一、Service 存在的意义 service的引入主…...
搭建samba服务
公司内部需要文件共享,自建samba服务,满足功能 在搭建过程中,踩了一些坑,如windows无法访问、macos无法访问、账号添加、权限控制 分享一下实现过程,内容不详细的地方,可评论或私聊 前置准备 服务器 阿里…...
总结vue3 的一些知识点:MySQL 排序
MySQL 排序 我们知道从 MySQL 表中使用 SQL SELECT 语句来读取数据。 如果我们需要对读取的数据进行排序,我们就可以使用 MySQL 的 ORDER BY 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。 语法 以下是 SQL SELECT 语句使用 ORDER B…...
从零开始:PHP实现阿里云直播的简单方法!
1. 配置阿里云直播的推流地址和播放地址 使用阿里云直播功能前,首先需要在阿里云控制台中创建直播应用,然后获取推流地址和播放地址。 推流地址一般格式为: rtmp://{Domain}/{AppName}/{StreamName}?auth_key{AuthKey}-{Timestamp}-{Rand…...
【数据结构】——二叉树特点
前言:我们前面已经了解了二叉树的一些概念,那么我们今天就来了解下二叉树的遍历实现和一些性质。 二叉树的遍历方式有三种:前序,中序,后序。 前序:先根节点,再左子树,最后右子树。 中…...
C++的类和对象(一)
目录 1、面向过程和面向对象初认识 2、为什么要有类 3、类的定义 类的两种定义方式 4、类的访问限定符 5、类的作用域 5.1 为什么要有作用域? 5.2类作用域 6、类的实例化 6.1类的实例化的定义 6.2类的实例化的实现 6.3经典面试题 7、类对象 7.1类对…...
基于单片机自动饮料混合机控制系统设计
**单片机设计介绍,基于单片机自动饮料混合机控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机自动饮料混合机控制系统设计是一个涉及多个领域的复杂项目,包括单片机技术、传感器技术…...
react-route-dom 实现简单的嵌套路由
最终效果 点击 to test1 点击to test2 > to test21 点击to test2 > to test22 代码如下 path: "page",element: <父组件 />,children: [{ path: "test1", element: <Test1 /> },{path: "test2",element: <Test2 />…...
万界星空科技灯具行业MES介绍
中国是LED照明产品最大的生产制造国,如今,我国初步形成了包括LED外延片的生产、LED芯片的制备、LED芯片的封装以及LED产品应用在内的较为完超为产业链,随着LED照明市场渗诱率的快速警升,LED下游应用市场将会越来越广阔。这也将推动…...
16进制字符串转字符串
一、浏览器上 function hexToUtf8(hexString) {const hexArray hexString.match(/.{1,2}/g) || [];const uint8Array new Uint8Array(hexArray.map(hex > parseInt(hex, 16)));const textDecoder new TextDecoder(GB2312); //可以切换字符编码return textDecoder.decode…...
pymysql.err.InternalError: (1054, “Unknown column ‘nan‘ in ‘field list‘“
记录在本地环境通过,然后在云环境,解决问题的过程; 最近两天遇到一个bug,具体就是在本地Pyhon环境运行成功,但是当放在云服务跑的时候,去屡屡报错,具体报错信息如下: pymysql.err.I…...
SQL 错误 [1476] [22012]: ORA-01476: 除数为 0
Oracle sql 语句 添加判断,如果分母为0,则查询结果为0,如果分母不为0,则返回查询结果 你可以使用条件表达式来实现这个要求。以下是一个示例的Oracle SQL查询语句,其中添加了判断条件来处理分母为0的情况:…...
go语言项目的目录结构
Golang 的项目目录结构并没有一个强制的标准,但社区中形成了一些共识和最佳实践,以便更好地组织和管理代码。以下是一个典型的 Golang 项目目录结构示例: /myproject ├── /cmd | ├── /app | | └── main.go | …...
Android : DataBinding 简化开发 简单应用
1.导包 ViewModel 用于观察数据 // 使用androidx版本库 ViewModelProviders implementation androidx.lifecycle:lifecycle-extensions:2.1.0-alpha032.在build.gradle 添加 在android 代码块中添加 复制后点更新(Sync Now) android{...// 步骤1.开启…...
计算机网络:应用层(下篇)
文章目录 前言一 、电子邮件(Email)1.邮件服务器2.SMTP[RFC 2821]3.邮件报文格式4.邮件访问协议 二、DNS(域名系统)1.DNS的历史2.DNS总体思路和目标(1)问题1:DNS名字空间(2ÿ…...
干货分享 | TSMaster小程序启动和停止的自动化控制流程
在实际应用场景中,用户常常需要按一定逻辑和时序来控制TSMaster内置功能模块的启动和停止,TSMaster软件内置有C/Python小程序和图形程序,开发者可以通过编程对这些模块的运行进行精确控制。本文将重点和大家分享一下如何通过C代码来控制TSMas…...
AI视频智能分析识别技术的发展与EasyCVR智慧安防视频监控方案
随着科技的不断进步,基于AI神经网络的视频智能分析技术已经成为了当今社会的一个重要组成部分。这项技术通过利用计算机视觉和深度学习等技术,实现对视频数据的智能分析和处理,从而为各个领域提供了广泛的应用。今天我们就来介绍下视频智能分…...
外包干了2个月,技术倒退2年。。。
先说一下自己的情况,本科生,20年通过校招进入深圳某软件公司,干了接近4年的功能测试,今年国庆,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...
书-用数组存储高于60低于70的人单独存起来
#include<stdio.h> # define N 10 //书-用数组存储高于60低于70的人单独存起来 int main(){float s[N]{68.2,62.3,63.4,34.5,45.6,56.7,67.8,78.9,89.0,100};int i;float diyu[100];int j0;for(i0;i<N;i){if(s[i]>60 && s[i]<70)diyu[j]s[i];//这里的范…...
三、DVP摄像头调试笔记(图片成像质量微调整,非ISP)
说明:当前调试仅仅用来测试和熟悉部分摄像头寄存器模式 一、图片成像方向控制,基本每个摄像头都会有上下左右翻转寄存器 正向图片 反向图片 二、设置成像数据成各种颜色,(黑白/原彩/黄色等等) 在寄存器书册描述中…...
网络营销与策划实训/长沙网站优化推广方案
结果为: [2,3] 和 [2] array.splice(start[, deleteCount[, item1[, item2[, …]]]]) splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。 start: 指定修改的开始位置(从0计数) i…...
在哪个网站上做推广作用好/搜索排名查询
项目中使用layui渲染表格,中间出现thymeleaf解析[[…]]问题 解决方案:修改为[ […] ]。 没错,中间加一个空格就行了,记录一下。...
wordpress redis更新/报个电脑培训班要多少钱
在今天的病毒里,需要谨慎防范“橘色诱惑”变种cht和“多面杀手”变种at。 英文名称:Trojan/Chifrax.cht 中文名称:“橘色诱惑”变种cht 病毒长度:88691字节 病毒类型:*** 危险级别:★★ 影响平台࿱…...
门户类网站有哪些/谷歌google官网
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼得到X,Y的数据点后,我尝试用origin7.5的analysis-fit sigmoidal功能拟合出曲线,由于方程太复杂,用公式计算很困难,我想用origin直接对这条曲线求出曲率最大的点,望高人指点…...
国内做网站哪家公司好/数据查询网站
问题: 有一个随机函数只生成0和1,生成0的概率是p,生成1的概率是1-p,如何操作能使得生成1和0的概率都是1/2? 解答: 因为调用两次随机函数,则00的概率是p*p,11的概率是(1-p)*(1-p), 0…...
chinacd.wordpress.som/西安网站建设哪家好
1、持久化对象的状态 【1】临时对象(Transient) 在使用代理主键的情况下, OID 通常为 null不处于 Session 的缓存中在数据库中没有对应的记录 【2】持久化对象(也叫”托管”)(Persist) OID 不为 null位于 Session 缓存中若在数…...