springcloud微服务架构搭建过程

news2024/11/24 4:10:42

项目地址:源代码

仅作为学习用例使用,是我开发过程中的总结、实际的一部分使用方式
开发环境:
jdk11
springboot2.7.6
springcloud2021.0.5
alibabacloud 2021.0.4.0
redis6.0
mysql8.0

一、项目搭建

1. 创建

wdz-api:存放远程服务调用相关接口
wdz-auth:认证业务
wdz-gateway:网关
wdz-modules:业务模块微服务
wdz-common:存放公用中间件、数据库等服务

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>
    <packaging>pom</packaging>
    <modules>
        <module>wdz-common</module>
        <module>wdz-gateway</module>
        <module>wdz-api</module>
        <module>wdz-auth</module>
        <module>wdz-modules</module>
    </modules>

    <groupId>com.wdz</groupId>
    <artifactId>wdz-ruzhou</artifactId>
    <version>1.0.0</version>
    <name>wdz-ruzhou</name>
    <description>微服务架构</description>
    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.6</spring-boot.version>
        <spring-cloud.version>2021.0.5</spring-cloud.version>
        <spring-cloud.alibaba>2021.0.4.0</spring-cloud.alibaba>
        <fastjson2.version>2.0.14</fastjson2.version>
        <lombok.version>1.18.24</lombok.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--springcloud 微服务依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--springboot 依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--alibaba 微服务依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud.alibaba}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!--配置打包环境数据-->
    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <profiles.active>dev</profiles.active>
                <nacos.username>nacos</nacos.username>
                <nacos.password>nacos</nacos.password>
                <nacos.server>127.0.0.1:8848</nacos.server>
                <nacos.discovery.namespace>5efed786-91f6-44c4-8e14-049df72b2a48</nacos.discovery.namespace>
                <nacos.config.namespace>5efed786-91f6-44c4-8e14-049df72b2a48</nacos.config.namespace>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>test</id>
            <properties>
                <profiles.active>test</profiles.active>
                <nacos.username>nacos</nacos.username>
                <nacos.password>nacos</nacos.password>
                <nacos.server>127.0.0.1:8848</nacos.server>
                <nacos.discovery.namespace>f02ccd30-e3d5-4235-9282-d0a613bd322b</nacos.discovery.namespace>
                <nacos.config.namespace>f02ccd30-e3d5-4235-9282-d0a613bd322b</nacos.config.namespace>
            </properties>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <profiles.active>prod</profiles.active>
                <nacos.username>nacos</nacos.username>
                <nacos.password>nacos</nacos.password>
                <nacos.server>127.0.0.1:8848</nacos.server>
                <nacos.discovery.namespace>747829ca-6d8a-4417-a46a-bd31a6f5e442</nacos.discovery.namespace>
                <nacos.config.namespace>747829ca-6d8a-4417-a46a-bd31a6f5e442</nacos.config.namespace>
            </properties>
        </profile>
    </profiles>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
        <!--用于yml配置中变量替换如: @nacos.name@-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <!-- 引入所有 匹配文件进行过滤 -->
                <includes>
                    <include>application*</include>
                    <include>bootstrap*</include>
                    <include>logback*</include>
                </includes>
                <!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

nacos(2.2.0.1)

我为什么选择nacos作为注册中心?
1、独立的可视化管理后台
2、持久化,配置数据直接存储在数据库中
3、动态配置,修改配置可实时生效,不用重启
4、活跃度高,资料文件丰富
5、功能全面
6、是经过阿里巴巴考验的
戳==>官网

安装nacos

点击选择版本下载
windows建议下载zip
linux 建议下载tar.gz
下载完成解压即可

配置数据库

打开conf下的application.properties文件:
修改数据库配置,将注释打开,并配置数据源
在这里插入图片描述

创建数据库目前只支持mysql,版本要求:5.6.5+

需要IPV6支持使用:derby-schema.sql 否则使用:mysql-schema.sql 配置完成后

单机启动nacos:

Linux/Unix/Mac使用命令:sh startup.sh -m standalone
windows使用命令:./startup.cmd -m standalone
standalone:表示单机模式运行,非集群模式
官方文档
启动过程中如果出现jwt认证问题启动失败,请参考:戳详情

错误示例
在这里插入图片描述
修改文件:

nacos.core.auth.plugin.nacos.token.secret.key
使用base64 处理数据长度大于等于32即可

在这里插入图片描述
在这里插入图片描述

访问:http://localhost:8848/nacos,默认用户名密码:nacos

集群启动 官方文档

如果是在一台电脑上/服务器上模拟集群:
将nacos解压之后的文件nacos复制多份,
在这里插入图片描述

并修改对应conf文件夹下的application.properties文件端口配置
在这里插入图片描述
再将conf下的cluster.conf.example 复制一份并修改后缀为conf
在这里插入图片描述
将其他nacos地址配置在cluster.conf中
在这里插入图片描述
依次启动服务:
windows命令:./startup.cmd
linux命令: sh ./startup.sh
访问对应nacos
在这里插入图片描述
在这里插入图片描述

nacos 服务配置config与发现 discovery

创建测试注册服务网关服务,nacos作为注册中心有多种方式,本文用javaSDK的方式
在这里插入图片描述

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>wdz-ruzhou</artifactId>
        <groupId>com.wdz</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.wdz</groupId>
    <artifactId>wdz-gateway</artifactId>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

yml配置:
server:
  port: 8080
  servlet:
    context-path: /

spring:
  application:
    name: wdz-gateway
  profiles:
    active: @profiles.active@
---
# 使用导入的方式读取所有需要的nacos中的配置文件
spring:
  config:
    import:
      - optional:nacos:application-common.yml
      - optional:nacos:${spring.application.name}.yml
  cloud:
    nacos:
      username: @nacos.username@
      password: @nacos.password@
      server-addr: @nacos.server@
      # 服务发现
      discovery:
        namespace: @nacos.discovery.namespace@
      # 配置
      config:
        namespace: @nacos.config.namespace@

集群nacos负载配置:

通过nginx配置服务访问的nacos

upstream nacos-cluster{
	server 127.0.0.1:8247;
	server 127.0.0.1:8549;
	server 127.0.0.1:8848;
}
server{
	listen 801;
	server_name localhost;
	location /nacos{
		proxy_pass http://nacos-cluster;
	}
}

结果

在这里插入图片描述

在这里插入图片描述

负载均衡Ribbon
内置负载均衡规则类规则描述
RoundRobinRule简单轮询服务列表选择服务器,它是Ribbon默认的负载均衡规则
AvailabilityFilteringRule忽略规则:1、链接3次失败,会标记为短路状态,将持续30秒,如果还是失败则持续短路,时间增长,2、并发数过高的服务链接,并发数达到上限则会被忽略
WeightedResponseTimeRule权重策略,每个服务器配置一个权重值,权重值越小,选择这个服务器的比重就会越小
ZoneAvooidanceRule以区域可用的服务器为基础进行服务器的选择,使用Zone对服务器进行分类,Zone相当于一个机房,一个区域,然后再对Zone内的多个服务做轮询
BestAvailableRule忽略哪些短路服务器,并选择并发数较低的服务器
RandomRule随机选择一个可用服务器
RetryRule重试机制的选择逻辑

自定义策略方式:

代码方式:作用的是所有服务
@Bean
public  IRule diyRule(){
	return new RandomRule();
}
yml方式:作用的是所配置的服务
servicename: # 服务名称如:userservice
	ribbon:
		NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 
	

ribbon默认是懒加载,第一次访问的时候响应时间会比较长,初次加载之后响应就会变短。
开启ribbon饥饿加载有效提高第一次访问响应时间

ribbon:
	eager-load:
		enabled: true  # 开启饥饿加载
		clients: # 指定加载的服务名称
			- userservice
			- orderservice
			- systemservice
			- XXXservice
nacos服务分级存储模型

一级是服务:如:userservice、orderservice
二级是集群:配置了同一个discovery.cluster-name:
三级是实例:如杭州机房的某台部署了要访问的服务器
设置实例的集群属性:名称一样的在同一个集群内
spring.cloud.nacos.discovery.cluster-name:

NacosRule负载均衡策略:
1、优先选择同集群的服务
2、本地集群找不到提供者,才去其他集群找,并且会报警告
3、确定了可用实例列表后,再采用随机负载均衡挑选实例

NacosRule配置:

servicename: # 服务名称如:userservice
	ribbon:
		NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule 

环境隔离:namespace

spring:
  cloud:
    nacos:
      discovery:
        namespace: @nacos.discovery.namespace@ 
# 不同名称空间之间的服务不能互通,如user使用的prod order使用的dev,这两个服务不能互通

nacos特点

1、支持服务端主动监测提供者状态,临时实例采用心跳模式,非临时实例采用主动监测模式
2、林实施例心跳不正常会被剔除nacos的服务列表,非临时实例不会被剔除
3、服务列表变更消息推送模式,服务列表更新及时
4、nacos集群默认采用AP方式,集群中存在非临时实例时,采用CP模式
CAP:C(一致性),A(可用性),P(分区容错)原文
在这里插入图片描述

Feign 源码

feign是声明式的http客户端

使用方式

pom引入依赖
<dependency>
   <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
java代码
@FeignClient("applicationName")
public interface ApplicationName{
	@GetMapping("/get")
	Object queryById(Long id);
}

自定义feign配置

类型作用说明
feign.Logger.Level修改日志级别包含四种不同的级别:none、basic、headers、full
feign.codec.Decoder响应结果解析器http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder请求参数编码将请求参数编码,便于通过http请求发送
feign.Contract支持注解格式默认是SpringMvc的注解
feign.Retryer失败重试机制请求失败的重试机制,默认没有,会使用Ribbon的重试

none: 不记录日志
basic:只记录请求方法和URL以及响应状态代码和执行时间
headers:记录基本信息以及请求和响应标头
full:记录请求和响应的头部、主体和元数据

yml日志配置

feign:
  client:
    config:
      default: # DETAULT 是全局配置,如果换成application.name 则是指定服务有效
        loggerLevel: FULL
feign:
  client:
    config:
      userservice: # 针对userservice有效
        loggerLevel: FULL

代码方式:

public class FeignClientConfig{
	@Bean
	public Logger.Level feignLogLevel(){
		return Logger.Level.BASIC;
	}
}
然后将该配置添加到:
全局配置方式启动类上
EnableFeignClients(defaultConfiguration=FeignClentConfig.class)
局部配置方式
@FeignClient(value="applicationname",configuration=FeignClientConfig)

feign性能优化

URLConnection:默认实现,不支持连接池
Apache HttpClient:支持链接池
OKHttp:支持连接池
因为http链接时要三次握手,断开时要4次挥手,导致的性能浪费
优化的性能主要包括:
1、使用连接池代替默认的URLConnection
2、日志级别,最好用basic或none,日志也会浪费性能

连接池配置:

引入依赖

<!--优化feignURLConnection-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

配置yml

feign:
  httpclient:
    enabled: true # 开启feign对httpClient的支持
    max-connections: 200 # 最大链接数  可根据测试结果最优配置
    max-connections-per-route: 50 # 每个路径最大链接数 可根据测试结果最优配置

实践:
在这里插入图片描述
system项目引用
EnableFeignClients.clients 指定feignClient接口,及默认配置
在这里插入图片描述

如果发现RemoteUserService 加载不到提示,spring-cloud-starter-loadbalancer依赖缺失
在api中添加依赖:负载均衡依赖

<dependency>
   <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

测试接口
在这里插入图片描述
目标服务接口
在这里插入图片描述

测试结果:
在这里插入图片描述

SpringAMQP

springboot封装的消息队列,使用的是rabbitmq

生产端

引入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>

配置

spring:
  rabbitmq:
    host: 127.0.0.1 # 主机
    port: 5672   # 端口
    virtual-host: /  # 虚拟主机
    username: rabbitmq # 用户名
    password: 123456  # 密码

消费端

引入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>

配置

spring:
  rabbitmq:
    host: 127.0.0.1 # 主机
    port: 5672   # 端口
    virtual-host: /  # 虚拟主机
    username: rabbitmq # 用户名
    password: 123456  # 密码

监听java代码:

@Component
public class RabbitMqListener {
    @RabbitListener(queues = "mq-user")
    public void listenerQueueMsg(String msg){
        System.out.println(msg);
    }
}

如果提示异常:队列不存在,且启动失败

添加配置:

@Configuration
public class RabbitMqConfig {
    @Bean
    public Queue initQueue(){
        return new Queue("mq-user");
    }
}

由于rabbitmq的消费预取机制,会导致处理能力弱的消费端处理速度变慢,所以限制预取信息条数

spring:
  rabbitmq:
	listener:
	      simple:
	        prefetch: 1 # 每次只能获取一条消息,处理完成之后才能获取下一个

监听端信息:

@Slf4j
@Component
public class RabbitMqListener {

    @RabbitListener(queues = "mq-user")
    public void listenerQueueMsg(String msg) {
        log.info("[listenerQueueMsg]=========:{}",msg);
    }

    @RabbitListener(queues = "mq-user")
    public void listenerQueueMsg2(String msg) {
        log.info("[listenerQueueMsg2]=========:{}",msg);
    }
}

消息发布/订阅

发布订阅模式允许将同一消息发送给多个消费者,实现方式是加入exchange(交换机):
fanout:广播
direct:路由
topic:话题
交换机只处理转发,会造成消息丢失

FanoutExchange 方式

发送消息用户服务:

@GetMapping("/send")
 public LoginUser send(String username) {
     String queueName = "demo.fanout";
     String message = username;
     rabbitTemplate.convertAndSend(queueName,"", message);
     return new LoginUser(1L, username, "123456");
 }

 @GetMapping("/to")
 public LoginUser to(String username) {
     String queueName = "demo.fanout";
     String message = username;
     rabbitTemplate.convertAndSend(queueName,"", message);
     return new LoginUser(1L, username, "123456");
 }

接收消息 system服务:

监听代码
 @RabbitListener(queues = "demo.fanout2")
 public void listenerFanoutMsg2(String msg) {
     log.info("[demo.fanout2]=========:{}", msg);
 }

@Configuration
public class FanoutExchangeConfig {

    /**
     * 一个消息多个消费者监
     * fanout 广播模式
     * 定义交换机
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("demo.fanout");
    }


    @Bean
    public Queue fanoutQueue() {
        return new Queue("demo.fanout");
    }

    /**
     * 绑定交换机与队列关系
     *
     * @param fanoutExchange
     * @param fanoutQueue
     * @return
     */
    @Bean
    public Binding fanoutBinding(FanoutExchange fanoutExchange,Queue fanoutQueue) {
        return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);
    }
}

接收消息 order服务:

监听
@Slf4j
@Component
public class RabbitMqListener {
    @RabbitListener(queues = "demo.fanout")
    public void listenerFanoutMsg(String msg) {
        log.info("[demo.fanout]=========:{}", msg);
    }
}

fanout 配置

@Configuration
public class FanoutExchangeConfig {

    /**
     * 一个消息多个消费者监
     * fanout 广播模式
     * 定义交换机
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("demo.fanout");
    }
    @Bean
    public Queue fanoutQueue() {
        return new Queue("demo.fanout2");
    }

    /**
     * 绑定交换机与队列关系
     *
     * @param fanoutExchange
     * @param fanoutQueue
     * @return
     */
    @Bean
    public Binding fanoutBinding(FanoutExchange fanoutExchange,Queue fanoutQueue) {
        return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);
    }
}

均可接收到消息

DirectExchange 方式

direct exchange 会将接收到的消息根据规则路由到指定的queue
每个queue都与exchange设置一个bindingkey
发布者发送消息时指定消息的RoutingKey
exchange将消息路由到BingingKey与消息RoutingKey一致的队列

order/system中的监听

// 注解方式处理监听信息
// bindings 代码实现的Binding
// @QueueBinding 绑定信息
// value = @Queue(name = "direct.queue")绑定消息队列
//  exchange = @Exchange(name = "demo.direct",type = ExchangeTypes.DIRECT)
// 绑定交换机,名称是demo.direct 类型是direct
// key 是direct模式的主要配置,可同时监听多个key
@RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue"),
            exchange = @Exchange(name = "demo.direct",type = ExchangeTypes.DIRECT),
            key = {"system","direct"}
    ))
    public void listenerMessage(String msg) {
        log.info("[system.direct]=========:{}", msg);
    }

消息发送者:

/**
  *@param exchange  交换机名称
  *@param message     消息内容
  *@param routingKey  direct 路由key
  *@return
  */
 @GetMapping("/direct")
 public void direct(String exchange,String message,String routingKey) {
     rabbitTemplate.convertAndSend(exchange,routingKey, message);
 }

发送信息:
http://localhost/user/direct?exchange=demo.direct&message=发送的消息内容&routingKey=system

Topic 方式

topic方式与direct方式类似,只是key由精准匹配转换为模糊匹配

 // 监听
 @RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "topic.queue"),
          exchange = @Exchange(name = "topic-wdz",type = ExchangeTypes.TOPIC),
          key = "#.order"
  ))
  public void listenerTopicMessage(String msg) {
      log.info("[listenerTopicMessage]=========:{}", msg);
  }

发送

@GetMapping("/direct")
public void direct(String exchange,String message,String routingKey) {
    rabbitTemplate.convertAndSend(exchange,routingKey, message);
}

优化

由于springamqp默认使用的是jdk默认的序列化(ObjectOutputStream)方式,性能问题有待提升。
更换为JSON方式序列化。

生产者/消费者引入依赖
  <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
   	  <artifactId>jackson-dataformats-binary</artifactId>
      <version>2.13.4</version>
  </dependency>

初始化bean即可

@Bean
publicMessageConverter messageConverter(){
    return new Jackson2JsonMessageConverter();
}

Sentinel

分布式事务seata

事务(transaction):遵守ACID原则

原子性(Atomicity):事务中的所有操作,要么全部成功,要么全部失败
一致性(consistency):要保证数据库内部完整性的约束、声明性约束
隔离性(isolation):对同一自愿操作的事务不能同时发生
持久性(durability):对数据库的一切修改将永久保存,不管是否出现故障

演示案例:

下单流程:
订单服务创建订单–>账户服务扣减余额–>商品服务扣减库存
1、访问订单服务创建订单
2、通过feign调用user扣减余额
3、通过feign调用goods扣减库存

在这里插入图片描述

feign接口

@FeignClient(value = "wdz-goods")
public interface RemoteGoodsService {

    @GetMapping("/goods/subtractStock")
    void subtractStock(@PathVariable("username") String username);

}
@FeignClient(value = "wdz-user")
public interface RemoteUserService {

    @GetMapping("/user/subtractBalance")
    void subtractBalance();
}

控制器代码

订单服务控制器

@RestController
@RequestMapping("order")
public class OrderController {

    @Autowired
    private RemoteGoodsService remoteGoodsService;
    @Autowired
    private RemoteUserService remoteUserService;

    @GetMapping("create")
    public void create(){
        System.out.println("订单服务:创建了订单");
        remoteGoodsService.subtractStock();
        remoteUserService.subtractBalance();
    }
}

用户服务控制器代码

@RestController
@RequestMapping("user")
public class UserController {
    @GetMapping("subtractBalance")
    public void subtractBalance(){
        System.out.println("用戶服务:减余额");
    }
}

商品控制器代码

@RestController
@RequestMapping("goods")
public class GoodsController {

    @GetMapping("subtractStock")
    public void subtractStock(){
        System.out.println("商品服务:减少了库存");
    }
}
依次启动nacos-->gateway-->user-->goods-->order
访问:http://localhost:8080/order/order/create
订单服务:创建了订单
用戶服务:减余额
商品服务:减少了库存

待续。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VS for Qt 向MySql 数据库中插入中文

问题&#xff1a; 今天我想向数据库中插入中文&#xff0c;但是&#xff0c;插入的时候会报错&#xff1a; 首先先看报错&#xff1a; QSqlError("1054", "QMYSQL: Unable to execute query", "Unknown column 韩红 in field list")如果错误码…

将Linux服务器上的项目上传至Github

使用git上传项目到github常规的步骤继续上传注意事项参考文章常规的步骤 初始化git空间 git init向缓冲区添加想要上传的文件 git add -f /data/xuhongbo/xuhongbo.code/unbiased_sgg_xuhongbo_BCL/maskrcnn_benchmark/*添加备注信息告诉机器&#xff0c;你真的要添加上述文…

elasticsearch-7.17.9

1、ElasticSearch 1.1、概念 1.1.1、分片(shard) 1、分片 在ES中所有数据的文件块&#xff0c;也是数据的最小单元块&#xff0c;整个ES集群的核心就是对所有分片的分布、索引、负载、路由等达到惊人的速度。 实列场景&#xff1a; 假设 IndexA 有2个分片&#xff0c;向 I…

数据结构 - 计数排序 | C

什么是计数排序 如上图&#xff0c;统计数组中值的个数&#xff1a; 2个[1]&#xff1a;1&#xff0c;1 1个[2]&#xff1a;2 3个[3]&#xff1a;3&#xff0c;3&#xff0c;3 2个[4]&#xff1a;4&#xff0c;4 传给原数组&#xff1a;&#xff08;即完成排序↓&#xff09; …

Fast R-CNN

目录 1. 关于 R-CNN 的缺点 2. Fast R-CNN 3. Fast R-CNN 算法的流程 3.1 CNN 特征提取 3.2 ROI pooling 3.3 Fast R-CNN 的输出 3.4 损失函数 4. Fast R-CNN的不足 1. 关于 R-CNN 的缺点 RCNN算法流程如下 RCNN算法分为四个步骤&#xff1a; SS 算法生成2000个候选框…

解除游戏多开限制,关闭互斥体句柄

游戏限制多开有很多种方法 比如说遍历窗口,遍历进程,配置文件,注册表,互斥体,mac地址,ip,公共文件,内存映射等等.方法很多.但是绝大部分游戏限制多开都是采用的互斥体.这节课我们来讲解一下关闭互斥体句柄来实现多开.例子为CQYH(这里的防护建议是,增加多种多开限制的方法 以及 …

系统安全与应用【上】

文章目录1.账号安全控制1.1 系统账号清理1.2 密码安全控制1.3 命令历史限制1.4 终端自动注销2.系统引导和登录控制2.1 使用su命令切换用户2.2 限制使用su命令的用户3.可插拔式认证模块PAM3.1 linux中的PAM安全认证3.2 PAM认证原理3.3 PAM认证的构成3.4 PAM安全认证流程3.5 使用…

(八)【软件设计师】计算机系统—浮点数

浮点数 浮点数。当机器字长为n时&#xff0c;定点数的补码和移码可表示2的n方个数&#xff0c;而其原码和反码只能表示2"-1个数&#xff08;0的表示占用了两个编码)&#xff0c;因此&#xff0c;定点数所能表示的数值范围比较小&#xff0c;在运算中很容易因结果超出范围而…

实力爆表,日日新成为AI领航者

目录正式发布自建算力SenseChat编程能力图像生成后言上周五&#xff0c;阿里发布大模型通义千问&#xff0c;正式开始邀请内测。本周一&#xff0c;人工智能巨头商汤科技正式发布“日日新”大模型体系&#xff0c;全面丰富的产品体系&#xff0c;多个功能表现超预期&#xff0c…

MobTech MobPush|不同手机厂商推送问题

配置了华为厂商推送&#xff0c;为什么有的华为设备无法接收离线消息 首先&#xff0c;排查配置的华为厂商参数是否正确&#xff1b; 其次&#xff0c;检查华为设备EMUI版本&#xff0c;低于5.0可能不支持&#xff0c;如果低于5.0&#xff0c;可以尝试升级设备里的‘华为移动服…

Google代码覆盖率最佳实践

软件质量保障: 所寫即所思&#xff5c;一个阿里质量人对测试的所感所悟。谷歌一直倡导的领域之一是使用代码覆盖率数据评估风险并识别测试中的真空。然而&#xff0c;代码覆盖率的价值一直是个争议的话题。每次聊到代码覆盖率时&#xff0c;似乎都会引发无尽的争论。由于大家固…

微信小程序开发 | 小程序开发框架

小程序开发框架7.1 小程序模块化开发7.1.1 模块7.1.2 模板7.1.3 自定义组件7.1.4插件7.2 小程序基础样式库—WeUI7.2.1 初识WeUI7.2.2【案例】电影信息展示7.3 使用vue.js开发小程序7.3.1 初识mpvue7.3.2 开发工具7.3.3 项目结构7.3.4【案例】计数器7.4 小程序组件化开发框架7.…

Ztree树状的处理

1.用一个div进行包裹ztree结构&#xff0c;引用相关的js代码和css样式&#xff0c;这里用的样式是awesome.css 所引用的js文件&#xff0c;css文件可以在网上下载&#xff08;这里所用到的jquery-ztree文件放在网盘了&#xff09; <ul id"tree" class"ztre…

企业即时通讯软件开发基本功能有哪些?

即时通讯是基于互联网技术的新型交流沟通方式&#xff0c;是目前最流行的通讯方式&#xff0c;广泛的应用市场使得各种各样的即时通讯软件系统也层出不穷&#xff0c;企业即时通讯就是其中的一种延伸。是一种面向企业终端使用者的网络营销、网络沟通和内容管理的工具服务&#…

mysql基础安装以及问题

mysql 基础安装以及问题安装MySQL8.0的安装&#xff1a;MySQL5.7 版本的安装、配置卸载服务的启动与停止图形化工具推荐安装 官网&#xff1a;https://www.mysql.com MySQL8.0的安装&#xff1a; 这里是写你直接要安装的目录&#xff1a; 之后配置mysql8.0 注意&#x…

不会注册ChatGPT?4个国内网站让你尽情体验

最近火出圈的科技新词非“ChatGPT”莫属了。 但是由于ChatGPT注册起来比较困难&#xff0c;我到现在都还学不会如何注册.... 但是&#xff01;世上无难事&#xff01;只要有心人&#xff01; 我千辛万苦终于找到几个ChatGPT平替的网站了。 AI中文智能对话 地址&#xff1a;…

目标检测入门:发展历史

预备知识 分类classification 输出:物体的类别 评估方法:准确率 定位location 输出:方框在图片中的位置(x,y,w,h) 评估方法:交并比IOU 定位的解决思路: 思路一:看做回归问题 看做回归问题,我们需要预测出(x,y,w,h)四个参数的值,从而得出方框的位置 成为class…

数字证书的相关专业名词(下)---OCSP及其java中的应用

一、前言 上篇文章我们了解了根证书和校验证书有效性中的一个比较重要的渠道–CRL&#xff0c;但是CRL有着时间延迟&#xff0c;网络带宽消耗等缺点&#xff0c;本篇文章我们了解另一种更高效也是目前被广泛应用的校验证书有效性的另一种方式–OCSP&#xff0c;并且我会结合ja…

4年测试工程师经历,下一步转开发还是继续测试?

​测试四年&#xff0c;没有积累编程脚本能力和自动化经验&#xff0c;找工作时都要求语言能力&#xff0c;自动化框架。感觉开发同事积累的经历容易找工作。下一步&#xff0c;想办法转开发岗还是继续测试&#xff1f;&#xff1f;&#xff1f;正常情况下&#xff0c;有了四年…

matlab图像处理系列:图片圈数识别+编号标记位置

matlab图像处理系列&#xff1a;图片圈数识别编号标记位置一、app界面介绍二、实现过程step1图像二值化step2 图像close 做差step3 像素阈值处理step4 清除小区域step5 识别联通区域 并在原图上标记三、项目分享一、app界面介绍 读取图片按钮&#xff1a;使用ui交互工具&#x…