SpringCloud微服务架构

[复制链接]
发表于 2026-2-12 04:29:01 | 显示全部楼层 |阅读模式
认识微服务

单体架构:将业务的全部功能会集在一个项目中开辟,打成一个包摆设。
​ 优点:


  • 摆设简单
  • 摆设本钱低
    缺点:
  • 耦合度高
  • 扩展性差
分布式架构:根据业务功能对体系举行拆分,每个业务模块作为独立项目开辟,称为一个服务。
​ 优点:


  • 低落服务耦合度
  • 有利于服务升级扩展
    缺点:
  • 架构复杂
  • 难度大
微服务微服务是一种颠末良好架构计划的分布式架构方案,微服务架构特性:


  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务本领,能做到单一职责,克制重复业务开辟
  • 面向服务:微服务对外袒露业务接口
  • 自治:团队独立、技能独立、数据独立、摆设独立
  • 隔离性强:服务调用做好隔离、容错、降级、克制出现级联题目
优点: 拆分粒度更小、服务更独立、耦合度更低
缺点: 架构非常复杂,运维、监控监控、摆设难度进步
(Dubbo、SpringCloud、SpringCloudAlibaba)微服务技能对比:

企业需求:

SpringCloud

SpringCloud是现在国内利用最广泛的微服务架构框架。它集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配。
官网地点:Spring Cloud

服务拆分及长途调用

服务拆分注意事项:

  • ​ 差别微服务,不要开辟雷同业务
  • ​ 微服务数据独立,不要访问其他微服务的数据库
  • ​ 微服务可以将自己的业务袒露为接口,提供给其他服务调用
实现夸长途服务调用利用RestTemplate

通过@Bean注解将RestTemplate对象交给spring管理,在须要的地方注入RestTemplate对象
调用方法:
  1.                 //利用RestTemplate发送http请求
  2.         //url路径
  3.         String url = "http://localhost:8081/user/"+ order.getUserId();
  4.         //发送http请求,实现远程调用
  5.         User user = restTemplate.getForObject(url, User.class);
复制代码
Eureka注册中央

服务调用出现的题目 : 1.调用服务地点硬编码 2.多个服务怎样选择 3.怎么知道服务提供者是否是康健状态
Eureka的作用:



  • 消耗者怎样向服务提供者提供详细信息的

    • 服务启动时向eureka注册自己的信息
    • eureka生存信息
    • 消耗者根据服务名称向eureka拉取提供者信息

  • 如果多个提供者,消耗者该怎样选择

    • 消耗者利用负载平衡算法,从服务列表中挑选一个

  • 消耗者怎样感知服务器提供者的康健状态

    • 服务提供者每隔30秒向EruekaServer发送哀求,陈诉康健状态
    • eureka会更新服务器列表,不正常得到提供者会被剔除

搭建EruekaServer

1.创建项目引入依靠
  1. <!--        eureak服务端-->
  2.         <dependency>
  3.             <groupId>org.springframework.cloud</groupId>
  4.             <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  5.         </dependency>
复制代码
2.编写启动类,添加@EnableEurekaServer注解
3.添加application.yml文件
  1. server:
  2.   port: 10086 #服务端口
  3. spring:
  4.   application:
  5.     name: eurekaserver
  6. eureka:
  7.   client:
  8.     service-url:  #eureka的地址信息
  9.       deafultZone: http://127.0.0.1:10086/eureka
复制代码
启动项目后方法10086端口就可以到eureka的界面
注册服务

将服务注册到eureka的步调:
1.在服务项目中引入spring-cloud-starter-netflix-eureka-client的依靠
  1. <!--        eureka客户端依赖-->
  2.         <dependency>
  3.             <groupId>org.springframework.cloud</groupId>
  4.             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5.         </dependency>
复制代码
2.在application.yml文件,编写设置
  1. server:
  2.   port: 8081
  3. spring:
  4.   datasource:
  5.     url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
  6.     username: root
  7.     password: ******
  8.     driver-class-name: com.mysql.jdbc.Driver
  9.   application:
  10.     name: userService #user服务名称
  11. mybatis:
  12.   type-aliases-package: cn.itcast.user.pojo
  13.   configuration:
  14.     map-underscore-to-camel-case: true
  15. logging:
  16.   level:
  17.     cn.itcast: debug
  18.   pattern:
  19.     dateformat: MM-dd HH:mm:ss:SSS
  20. eureka:
  21.   client:
  22.     service-url:  #eureka的地址信息
  23.       defaultZone: http://127.0.0.1:10086/eureka
复制代码
服务发现

1.在哀求路径上面把路径换成我们注册服务的名称
  1. public Order queryOrderById(Long orderId) {
  2.         // 1.查询订单
  3.         Order order = orderMapper.findById(orderId);
  4.         //利用RestTemplate发送http请求
  5.         //url路径(将路径换成需要的服务的名称)
  6.         String url = "http://userservice/user/"+ order.getUserId();
  7.         //发送http请求,实现远程调用
  8.         User user = restTemplate.getForObject(url, User.class);
  9.         order.setUser(user);
  10.         // 4.返回
  11.         return order;
  12.     }
复制代码
2.加上RestTemplate的Bean上面加 @LoadBalanced 注解
Ribbon负载平衡


ribbon的负载平衡战略


修改负载平衡规则

默认是轮询方式
1.代码方式:在消耗端的Application中,界说一个新的IRule
  1. @Bean
  2. public IRule randomRule(){
  3.         return new RandowRule();
  4. }
复制代码
2.设置文件方式:设置文件只是更改一个服务的负载,不是全局
  1. userservice:
  2.         ribbon:
  3.                 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  4.                
复制代码
解饿加载

Ribbon默认是接纳懒加载,即第一次加载时才会去创建LoadBalanceClient,哀求时间会很长。而解饿加载则会在项目启动时创建,低落第一次访问的耗时。
  1. ribbon:
  2.   eager-load:
  3.     enabled: true #开启解饿加载
  4.     clients:
  5.       -userservice #指定服务名称,多个服务需要在下面加 -
复制代码
Nacos注册中央(nacos一部分功能)

Nacos是阿里巴巴的产物,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富。
Windows启动nacos下令:startup.cmd -m standalone
服务注册到nacos

1.在Cloud-demo父工程中添加Spring-cloud-alilbaba 的管理依靠:
  1.     <dependency>
  2.          <groupId>com.alibaba.cloud</groupId>
  3.          <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  4.          <version>2.2.5.RELEASE</version>
  5.          <type>pom</type>
  6.          <scope>import</scope>
  7.      </dependency>
复制代码
2.添加nacos的客户端依靠
  1. <!--        nacos客户端依赖包-->
  2.         <dependency>
  3.             <groupId>com.alibaba.cloud</groupId>
  4.             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  5.         </dependency>
复制代码
3.改写application.yml 文件,向yml文件中参加nacos的服务地点设置
  1.   application:
  2.     name: userservice #user服务名称
  3.   cloud:
  4.     nacos:
  5.       discovery:
  6.         server-addr: localhost:8848 #nacos服务地址
复制代码
nacos服务分级存储模子

添加服务集群属性
修改application.yml
  1.   cloud:
  2.     nacos:
  3.       discovery:
  4.         server-addr: localhost:8848 #nacos服务地址
  5.         cluster-name: SH #集群名称,Hz代指杭州
复制代码
更改服务访问规则(NacosRule负载平衡规则)
  1. userservice:
  2.   ribbon:
  3.     NFLoadBalancerRuleClassName: com.alibaba.nacos.ribbon.NacosRule
复制代码
NacosRule负载平衡规则 : 集群优先
情况隔离 -namespace

1.在nacos网站去创建namespace定名空间

2.设置服务地点定名空间,在application.yml文件中修改
  1.   cloud:
  2.     nacos:
  3.       discovery:
  4.         server-addr: localhost:8848
  5.         cluster-name: HZ
  6.         namespace: 359d6479-2271-43fa-9ca3-9cf1f1082906 #dev环境(复制我们创建好的命名空间Id)
复制代码
差别nameSpace下的服务不可见(不能访问)

Nacos和eureka的区别

Nacos设置管理

设置更改热更新

同一设置管理
1.引入Nacos的设置管理客户端依靠:
  1. <!--        nacos的配置管理依赖-->
  2.         <dependency>
  3.             <groupId>com.alibaba.cloud</groupId>
  4.             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  5.         </dependency>
复制代码
2.在服务提供者中的resource目次中添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml文件
  1. spring:
  2.   application:
  3.     name: userservice #服务名称
  4.   profiles:
  5.     active: dev #环境
  6.   cloud:
  7.     nacos:
  8.       discovery:
  9.         server-addr: localhost:8848 #nacos地址
  10.       config:
  11.         file-extension: yaml #文件后缀名
复制代码
将application.yml文件中重复的设置删掉

设置自动革新


  • 方式一:在@Value注入的变量地点类上添加注解@RefreshScope
  • 方式二:利用@ConfigurationProperties注解
  1. @Data
  2. @Component
  3. @ConfigurationProperties(prefix = "pattern")
  4. public class PatternProperties {
  5.     private String dateformat;
  6. }
复制代码
多情况设置共享优先级:
服务名-profile.yaml > 服务名.yaml > 当地设置
热更新的Springboot实现
1.导入jar包
  1.         <dependency>
  2.             <groupId>com.alibaba.boot</groupId>
  3.             <artifactId>nacos-config-spring-boot-starter</artifactId>
  4.             <version>0.2.11</version>
  5.         </dependency>
复制代码
2.更改application.yaml文件
  1. nacos:
  2.   config:
  3.     data-id: jc-club-oss  #用于指定要获取的配置数据的ID或名称。
  4.     secret-key: nacos     # 用于进行访问控制的密钥,用于对配置中心的访问进行认证和授权。
  5.     access-key: nacos   # 用于进行访问控制的密钥,用于对配置中心的访问进行认证和授权。
  6.     group: DEFAULT_GROUP  #用于指定配置数据所属的分组,这样可以更好地组织和管理配置。
  7.     type: yaml        #指定配置数据的类型,例如 YAML、Properties 等。
  8.     server-addr: http://117.72.14.166:8848/  #指定Nacos配置中心的地址,包括主机名和端口。
  9.     auto-refresh: true    #标识是否自动刷新配置内容,当配置中心的配置发生变化时,客户端是否自动更新配置。
  10.     remote-first: true    #当配置中心不可用时,是否优先使用本地缓存的配置。
  11.     bootstrap:
  12.       enable: true    #是否启用Bootstrap配置,Bootstrap配置是在Spring应用程序启动时首先加载的一组配置。
复制代码
3.利用@NacosValue注解
  1.     @NacosValue(value = "${storage.service.type}",autoRefreshed = true)
  2.     private String storageType;
  3.     @GetMapping("/testNacos")
  4.     public String testNacos() {
  5.        return storageType;
  6.     }
复制代码
出现 failed to req API:/nacos/v1/ns/instance after all servers([localhost:8848]) tried: java.net.ConnectException: Connection refused: connect 非常
由于同时导入了Nacos的设置中央和注册中央的依靠导致
Nacos设置中央依靠 -config 注册中央 -discovery
http客户端Feign

利用之前的RestTemplate方式调用会出现一些题目


  • 代码可读性差,编程体验不同一
  • 参数复杂URL难以维护
Feign是一声明式的http客户端,作用是资助我们优雅的实现http哀求的发送。
1.引入Feign依靠
  1. <!-- feign客户端依赖-->
  2. <dependency>
  3.      <groupId>org.springframework.cloud</groupId>
  4.      <artifactId>spring-cloud-starter-openfeign</artifactId>
  5.     <version>2.2.7.RELEASE</version>
  6. </dependency>
复制代码
2.在启动类上添加注解开启Feign的功能
  1. @MapperScan("cn.itcast.order.mapper")
  2. @SpringBootApplication
  3. @EnableFeignClients
复制代码
3.编写Feign客户端
  1. @FeignClient("userservice")
  2. public interface UserClient {
  3.     @GetMapping("/user/{id}")
  4.     User findById(@PathVariable("id") Long id);
  5. }
复制代码
注意:若出现 Load balancer does not have available server for client: userservice 错误 须要查察服务的定名空间是否划一
微服务之间用openfeign调用要想包管微服务之间的用户上下信息划一须要添加feign的拦截器
  1. @Component
  2. public class FeignRequestInterceptor implements RequestInterceptor {
  3.     @Override
  4.     public void apply(RequestTemplate requestTemplate) {
  5.         ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  6.         HttpServletRequest request = requestAttributes.getRequest();
  7.         if (!ObjectUtils.isEmpty(request)) {
  8.             String loginId = request.getHeader("loginId");
  9.             if (StringUtils.isNotBlank(loginId)){
  10.                 requestTemplate.header("loginId",loginId);
  11.             }
  12.         }
  13.     }
  14. }
复制代码
同时将FeignRequestInterceptor注册成Bean交给Spring管理
  1. @Configuration
  2. public class FeignConfiguration {
  3.     @Bean
  4.     public RequestInterceptor requestInterceptor(){
  5.         return new FeignRequestInterceptor();
  6.     }
  7. }
复制代码
如许其他微服务就可以通过拦截器拿到loginId
自界说Feign的设置


设置日志日志级别方式一(修改设置文件的方式):
  1. feign:
  2.   client:
  3.     config:
  4.       default:
  5.               # 设置日志日志记录级别,其取值共有none、basic、headers、full
  6.         loggerLevel: FULL
复制代码
设置日志日志级别方式二(声明Bean的方式):
  1. public class DefaultFeignConfiguration {
  2.     @Bean
  3.     public Logger.Level logLevel(){
  4.         return Logger.Level.BASIC;
  5.     }
  6. }
复制代码
全局有用
  1. @EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
复制代码
详细服务有用
  1. @FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)
复制代码
Feign的性能优化


利用毗连池取代默认的URLConnection:
Feign添加HttpClient的支持依靠:
  1. <!--        引入HttpClient依赖-->
  2.         <dependency>
  3.             <groupId>io.github.openfeign</groupId>
  4.             <artifactId>feign-httpclient</artifactId>
  5.         </dependency>
复制代码
设置application.yml文件:
  1. feign:
  2.   httpclient:
  3.     enabled: true  #支持httpClient的开关
  4.     max-connections: 200  #最大连接数
  5.     max-connections-per-route: 50  #单个路径的最大连接数
复制代码
Feign的优化:


  • 日志级别只管用basic
  • 利用HttpClient大概OkHttp取代URLConnection
Feign的最佳实践

方式二:

  • 新建module并引入feign的starter的依靠
    1.         <dependency>
    2.             <groupId>org.springframework.cloud</groupId>
    3.             <artifactId>spring-cloud-starter-openfeign</artifactId>
    4.         </dependency>
    复制代码
  • 将feign的客户端和实体类抽取到新建的module中
  • 在须要利用服务的pom文件中引入依靠
  • 修改组件有关的import部分,改成利用新建module中的包

同一网关Gateway

网关功能:



  • 身份认证和权限校验
  • 服务路由,负载平衡
  • 哀求限流
Springcloud 中网关的实现包罗两种:


  • gateway
  • zuul
搭建网关服务:

1.创建一个module,引入SpringCloudGateway的依靠和Nacos的服务发现依靠:
  1. <!--        nacos服务发现依赖-->
  2.         <dependency>
  3.             <groupId>com.alibaba.cloud</groupId>
  4.             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  5.         </dependency>
  6. <!--        网关gateway依赖-->
  7.         <dependency>
  8.             <groupId>org.springframework.cloud</groupId>
  9.             <artifactId>spring-cloud-starter-gateway</artifactId>
  10.         </dependency>
复制代码
2.编写路由设置以及Nacos地点
  1. server:
  2.   port: 10010 #网关端口
  3. spring:
  4.   application:
  5.     name: gateway #服务名称
  6.   cloud:
  7.     nacos:
  8.       server-addr: localhost:8848
  9.     gateway:
  10.       routes: #网关路由配置
  11.         - id: user-service #路由Id,自定义,只要唯一即可
  12.           #uri: http://127.0.0.1:8081 #路由的目标地址http是固定地址
  13.           uri: lb://userservice #路由目标地址 lb就是负载均衡,后面跟服务名称
  14.           predicates: #路由断言,也就是判断请求是否符合路由规则的条件
  15.             - Path=/user/** #按照路径匹配,只要以/user/开头就符合条件
  16.         - id: order-service
  17.           uri: lb://orderservice
  18.             predicates:
  19.               - Path=/order/**
复制代码

总结:
网关搭建步调:

  • 创建项目,引入nacos服务发现和gateway依靠
  • 设置application.yml,包罗服务根本信息、nacos地点、路由
路由设置包罗:

  • 路由id:路由的唯一标示
  • 路由目的(uri):路由的目的地点,http代表固定地点,lb代表根据服务名负载平衡
  • 路由断言(predicates):判定路由的规则,
  • 路由过滤器(filters):对哀求或相应做处理惩罚
路由断言工厂Route Predicate Factory

我们在设置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理惩罚,变革为路由判定的条件
比方Path=/user/**是按照路径匹配,这个规则是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来
处理惩罚的,像如许的断言工厂在SpringCloudGateway尚有十几个:
名称分析示例After是某个时间点后的哀求- After=2037-01-20T17:42:47.789-07:00[America/Denver]Before是某个时间点之前的哀求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]Between是某两个时间点之前的哀求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]Cookie哀求必须包罗某些cookie- Cookie=chocolate, ch.pHeader哀求必须包罗某些header- Header=X-Request-Id, \d+Host哀求必须是访问某个host(域名)- Host=.somehost.org,.anotherhost.orgMethod哀求方式必须是指定方式- Method=GET,POSTPath哀求路径必须符合指定规则- Path=/red/{segment},/blue/**Query哀求参数必须包罗指定参数- Query=name, Jack大概- Query=nameRemoteAddr哀求者的ip必须是指定范围- RemoteAddr=192.168.1.1/24Weight权重处理惩罚过滤器工厂

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的哀求和微服务返回的相应做处理惩罚:

名称分析AddRequestHeader给当前哀求添加一个哀求头RemoveRequestHeader移除哀求中的一个哀求头AddResponseHeader给相应效果中添加一个相应头RemoveResponseHeader从相应效果中移除有一个相应头RequestRateLimiter限定哀求的流量哀求头过滤器

下面我们以AddRequestHeader 为例来解说。
   需求:给全部进入userservice的哀求添加一个哀求头:Truth=itcast is freaking awesome!
  只须要修改gateway服务的application.yml文件,添加路由过滤即可:
  1. spring:
  2.   cloud:
  3.     gateway:
  4.       routes:
  5.       - id: user-service
  6.         uri: lb://userservice
  7.         predicates:
  8.         - Path=/user/**
  9.         filters: # 过滤器
  10.         - AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
复制代码
当前过滤器写在userservice路由下,因此仅仅对访问userservice的哀求有用。
默认过滤器

如果要对全部的路由都见效,则可以将过滤器工厂写到default下。格式如下:
  1. spring:
  2.   cloud:
  3.     gateway:
  4.       routes:
  5.       - id: user-service
  6.         uri: lb://userservice
  7.         predicates:
  8.         - Path=/user/**
  9.       default-filters: # 默认过滤项
  10.       - AddRequestHeader=Truth, Itcast is freaking awesome!
复制代码
自界说全局过滤器

需求:界说全局过滤器,拦截哀求,判定哀求的参数是否满足下面条件:


  • 参数中是否有authorization,
  • authorization参数值是否为admin
假如同时满足则放行,否则拦截
实现:
在gateway中界说一个过滤器:
  1. package cn.itcast.gateway.filters;
  2. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  3. import org.springframework.cloud.gateway.filter.GlobalFilter;
  4. import org.springframework.core.annotation.Order;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.stereotype.Component;
  7. import org.springframework.web.server.ServerWebExchange;
  8. import reactor.core.publisher.Mono;
  9. @Order(-1)
  10. @Component
  11. public class AuthorizeFilter implements GlobalFilter {
  12.     @Override
  13.     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  14.         // 1.获取请求参数
  15.         MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
  16.         // 2.获取authorization参数
  17.         String auth = params.getFirst("authorization");
  18.         // 3.校验
  19.         if ("admin".equals(auth)) {
  20.             // 放行
  21.             return chain.filter(exchange);
  22.         }
  23.         // 4.拦截
  24.         // 4.1.禁止访问,设置状态码
  25.         exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
  26.         // 4.2.结束处理
  27.         return exchange.getResponse().setComplete();
  28.     }
  29. }
复制代码
过滤器实验次序

哀求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
哀求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,归并到一个过滤器链(聚集)中,排序后依次实验每个过滤器:

排序的规则:


  • 每一个过滤器都必须指定一个int范例的order值,order值越小,优先级越高,实验次序越靠前
  • GlobalFilter通过实现Ordered接口,大概添加@Order注解来指定order值,由我们自己指定
  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明次序从1递增。
  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的次序实验。
详细内容,可以查察源码:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后归并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器归并后根据order排序,构造过滤器链
办理跨域题目

在gateway服务的application.yml文件中,添加下面的设置:
  1. spring:
  2.   cloud:
  3.     gateway:
  4.       # 。。。
  5.       globalcors: # 全局的跨域处理
  6.         add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
  7.         corsConfigurations:
  8.           '[/**]':
  9.             allowedOrigins: # 允许哪些网站的跨域请求
  10.               - "http://localhost:8090"
  11.             allowedMethods: # 允许的跨域ajax的请求方式
  12.               - "GET"
  13.               - "POST"
  14.               - "DELETE"
  15.               - "PUT"
  16.               - "OPTIONS"
  17.             allowedHeaders: "*" # 允许在请求中携带的头信息
  18.             allowCredentials: true # 是否允许携带cookie
  19.             maxAge: 360000 # 这次跨域检测的有效期
复制代码
网关提示503错误,而且设置的地点信息都符合 查察是否设置的负载平衡的依靠
  1.         <dependency>
  2.             <groupId>org.springframework.cloud</groupId>
  3.             <artifactId>spring-cloud-loadbalancer</artifactId>
  4.         </dependency>
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表