SpringCloud微服务netflix方案

SpringCloud-Project-Netflix方案

  • Eureka 注册中心:独立服务端
  • OpenFeign 远程服务调用
  • Ribbon 负载均衡
  • Hystrix 断路器
  • Gateway 网关中心:独立服务端
  • Config 配置中心:独立服务端

第一部分:各个模块部分

1、添加依赖
<!-- Eureka 注册中心客户端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--Openfeign 远程RPC,注意已经包含了Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>
<!-- Hystrix 断路器-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix</artifactId>
	<version>1.4.7.RELEASE</version> 
</dependency>
<!--config 配置中心-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--actuator 健康检查-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--RabbitMQ消息总线,用于刷新配置-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!--使用zuul网关-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<!--ziplink 服务端以及UI界面-->
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-server</artifactId>
    <version>2.4.3</version>
</dependency>
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-autoconfigure-ui</artifactId>
    <version>2.4.3</version>
</dependency>
2、Eureka-Config-gateway客户端配置

默认配置

  • client.refresh.interval=30s
  • client.heartbeat.threadPoolSize=5
  • client.heartbeat.exponentialBackOffBound=10
  • client.cacheRefresh.threadPoolSize=5
  • client.cacheRefresh.exponentialBackOffBound=10
server:
  port: 8089
spring:
  application:
    name: foobar
  # 启用压缩
  compression: 
    enabled: true 
    mime-types: ["text/html", "text/xml", "text/plain", "text/css", "text/javascript", "application/javascript", "application/xml", "application/json"]
    min-response-size: 512
  cloud:
    ## 连接配置中心
    config:
      uri: http://localhost:12000 # 配置中心的具体地址,即 config-server
      name: config-client # 对应 {application} 部分
      profile: dev # 对应 {profile} 部分
      label: master # 对应 {label} 部分,即 Git 的分支。如果配置中心使用的是本地存储,则该参数无用
      username: user
      password: password123
      discovery:
         enabled: true
         service-id: microservice-config-server-eureka
      ## 配置中心消息总线 RabbitMQ配置
      rabbitmq:
        host: localhost
        port: 5672
        username: guest
        password: guest
# 注册中心
eureka:
  client: # Eureka客户端配置,放在Eureka服务端
    # 关闭eureka client
    enabled: false
    # 注册自身到eureka服务器,默认是true
    register-with-eureka: true
    # 表示是否从eureka服务器获取注册信息,默认是false
    fetch-registry: false
    # 客户端从Eureka Server集群里更新Eureka Server信息的频率
    eureka-service-url-poll-interval-seconds: 60
    # 定义从注册中心获取注册服务的信息
    registry-fetch-interval-seconds: 5
    # 设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址
    serviceUrl:
       # 设置eureka服务器所在的地址,可以同时向多个服务注册服务
      defaultZone: http://127.0.0.1:8000/eureka/

# 远程调度服务
feign:
  compression:  #请求压缩
    request:
      enabled: true
      mime-types: ["text/xml", "application/xml", "application/json"]
      min-request-size: 512
    response:
      enabled: true
      ## 使用HTTPClient 
      httpclient:
        enabled: true  # 使用httpClient
        maxConnections: 20480  # 最大连接时间
        maxConnectionsPerRoute: 512  # 单个路径最大连接数
        timeToLive: 60
        connectionTimeout: 10000   # 连接超时时间
      ## 使用OkHttp 注意:HTTPClient和OKHttp只能二选一
      okhttp:
        enabled: false
        maxConnections: 20480  # 最大连接时间
        maxConnectionsPerRoute: 512  # 单个路径最大连接数
        timeToLive: 60
        connectionTimeout: 10000   # 连接超时时间
  client:
      config:
        default:   # key 为default时表示的是全局配置
          loggerLevel: basic  # 配置日志级别
        product-provider-01: # 为具体的服务提供者的spring.application.name的值,表示单独为这个工程配置
          loggerLevel: full
#Ribbon负载均衡配置
ribbon:
  ConnectTimeout: 2000 # 请求连接的超时时间
  ReadTimeout: 5000 # 请求处理的超时时间
  OkToRetryOnAllOperations: true

## 服务的负载均衡策略
foobar:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #设置负载均衡
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule # 权重规则
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #设置负载均衡
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #设置负载均衡
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #设置负载均衡
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.AvailabilityFilteringRule # 可用服务规则
    #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule # 区域可用规则
## 日志
logging:
  level:
    com:
      tuling:
        feignapi: DEBUG
3、启动类
@SpringBootApplication
public class ConfigServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ConfigServerApplication.class, args);
  }
}
4、全局配置类

@EnableDiscoveryClient 注册中心客户端启动
@EnableHystrix 断路器
@EnableCircuitBreaker 断路器
@RefreshScope 配置中心自动刷新
@EnableFeignClients RPC消费端

@Configuration
@EnableDiscoveryClient 
@EnableHystrix
@EnableCircuitBreaker
@RefreshScope
@EnableFeignClients
public class GlobalConfig{

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    // 如果使用httpClient连接池配置
    @Bean
    public HttpClient httpClient(){
        // 生成默认请求配置
        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        requestConfigBuilder.setSocketTimeout(5 * 1000);
        requestConfigBuilder.setConnectTimeout(5 * 1000);
        RequestConfig defaultRequestConfig = requestConfigBuilder.build();
        //请求连接池
        final PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.MILLISECONDS);
        pollingConnectionManager.setMaxTotal(5000);
        pollingConnectionManager.setDefaultMaxPerRoute(100);
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig);
        HttpClient client = httpClientBuilder.build();
        // 启动定时器,定时回收过期的连接, 最重要。 如果没有定义回收策略。连接池会在运行一段时间后失效。
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                pollingConnectionManager.closeExpiredConnections();
                pollingConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS);
            }
        }, 10 * 1000, 5 * 1000);
        System.out.println("===== Apache httpclient 初始化连接池===");
        return client;
    }
}
5、FeignCliet服务消费端
  • @FeignClient 注解作用是指定调用的外部的接口
    • name 对外的服务名称,这里和eureka上注册名称相同
    • value 同name
    • contextId 指定唯一的名称
    • url 选填,如果填写后,会指定调用某个服务
    • qualifier 判断
    • decode404 404解码
    • configuration 自定义配置
    • fallback 失败回调
    • fallbackFactory 失败回调工厂类
    • path 类似于@RequestMapping用于类上地址
    • primary 是否主要
  • fallbackFactory 服务降级
  • fallback 服务降级 二选一
// 指定user-center
@FeignClient(contextId = "user-center",name = "UserClient", url = "http://127.0.0.1:8001",
            fallbackFactory = HelloRollBackFactory.class,
            fallback = HystrixClientFallback.class)
public interface UserCenterClient {
	//注意这里需要对应的是服务提供者的接口相对应
    @RequestMapping(method = RequestMethod.GET, value = "/participate")
	String getUserByForm(@RequestParam Map<String, Object> params);
}
//服务降级
@Slf4j
@Component
public class HelloRollBackFactory implements FallbackFactory<HelloService> {

    @Override
    public HelloService create(Throwable throwable) {
        //可以抓取异常
        log.info("fallback; reason was: {}", throwable.getMessage());
        //返回实现的一个回调接口
        return new HelloService(){
            @Override
            public User findById(String name) {
                log.info("UserFeignClient#findById#id={}服务降级了",id);
                User user = new User();
                user.setId(0L);
                return user;
            }
        }
    }
}
//服务降级2
@Slf4j
@Component
public class HystrixClientFallback implements UserFeignClient {
  //回调类,返回一个默认的user,防止调用出现空指针的问题
  @Override
  public User findById(Long id) {
    log.info("UserFeignClient#findById#id={}服务降级了",id);
    User user = new User();
    user.setId(0L);
    return user;
  }
}

6、使用FeignClient
public class courseService{
    
	@Autowired
	private UserCenterClient userCenterClient;
	
	public Result useClien(){
        //省略参数
		return userCenterClient.getUserByForm(....);
	}
}

第二部分:独立服务搭建

一、Eureka注册中心客户端
  • 可以搭建集成环境
1、添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2、Eureka服务端配置添加配置
  • appinfo.replicate.interval=30s
  • appinfo.initial.replicate.time=40s
  • serviceUrlPollIntervalMs=30s
  • eurekaServer.readTimeout=8s
  • eurekaServer.connectTimeout=5s
  • eurekaServer.maxTotalConnections=200s
  • eurekaServer.maxConnectionsPerHost=50
  • eurekaserver.connectionIdleTimeoutInSeconds=30s
## Eureka服务端配置
eureka:
  instance:
    hostname: T231
    # 使用IP注册,Spring就会自动为我们获取第一个非回环IP地址
    prefer-ip-address: true
    # 心跳间隔
    lease-renewal-interval-in-seconds: 3
    # 服务失效时间: 如果多久没有收到请求,则可以删除服务,默认是30s
    lease-expiration-duration-in-seconds: 7
   server: #Eureka服务端配置,放在Eureka客户端
     # renewal-percent-threshold: 0.1
     # 关闭自我保护模式
     enable-self-preservation: false
     # Eureka Server 自我保护系数,当enable-self-preservation=true时,启作用
     # renewal-percent-threshold:
     # 设置清理间隔,单位为毫秒,默认为0
     eviction-interval-timer-in-ms: 3000
     # 设置如果Eureka Server启动时无法从临近Eureka Server节点获取注册信息,它多久不对外提供注册服务
     wait-time-in-ms-when-sync-empty: 6000000
     # 集群之间相互更新节点信息的时间频率
     peer-eureka-nodes-update-interval-ms: 60000
3、启动类

启动类添加@EnableEurekaServer

二、配置中心

注意配置中心也需要作为Eureka的客户端,为避免重复,这里相关的依赖和配置省略
使用时需要注意

1、添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、添加配置项

环境配置规则

  • application:配置通用名:例如application
  • profile:处理名例如dev
  • label: 对应git的分支内容,例如master
    /{application}/{profile}[/{label}]
    /{application}-{profile}.yml
    /{label}/{application}-{profile}.yml
    /{application}-{profile}.properties
    /{label}/{application}-{profile}.properties
spring:
  cloud:
    config:
      server:
        git:
          uri: https://git.oschina.net/it-much/config-repo-51cto-video   # 公用
          # uri: https://git.oschina.net/it-much/{application}   #通配符
          username:  #账号
          password:  #密码
          repos:
            simple: https://git.oschina.net/it-much/simple 
            special:
              pattern: special*/dev*,special*/test*  # 模式
              uri: https://git.oschina.net/it-much/special
              cloneOnStart: false  # 关闭cloneOnStart
          search-paths:     # 搜索路径
            - foo   # foo路径
            - bar   # bar路径
## 为配置中心添加安全访问
security:
  basic:
    enabled: true
  user:
    name: user
    password: password123
3、启动类添加注解
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(ConfigServerApplication.class, args);
  }
}
4、测试获取配置

浏览器打开:http://localhost:12000/config-client/dev

三、网关中心Gateway

网关中心服务需要链入Eureka

1、添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2、添加配置项
  • datetime :请求时间限制在之前,之间,之后
    • After=2018-01-20T06:06:06+08:00[Asia/Shanghai]
    • Before=2018-01-20T06:06:06+08:00[Asia/Shanghai]
    • Between=2018-01-20T06:06:06+08:00[Asia/Shanghai]
  • cookie :请求指定的Cookie
    • Cookie=ityouknow, kee.e
  • header :请求指定的Header的正则配置和指定名称
    • Header=X-Request-Id, \d+
  • host :请求指定的Host
    • Host=**.ityouknow.com
  • method :请求指定的方法
    • Method=GET,POST,OPTIONS
  • path :请求指定的路径
    • Path=/foo/{segment}
  • QueryParam :请求指定的参数值
    • Query=channel_web
  • RemoteAddr :请求指定的远程地址
    • RemoteAddr=192.168.1.1/24
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true   		#开启gateway的客户端发现
      routes:					#路由规则
        - id: course      		#路由规则
          uri: /v1/api/course  	#映射地址
          order: 1    			#排序
          predicates:			#配置预处理器
            - Path=/**
          filters:				#配置过滤器
            - SetPath=
            - RewritePath=/CONSUMER/(?<segment>.*), /$\{segment}
            - StripPrefix=2 # 请求路径修改过滤器 StripPrefix=2就代表截取路径的个数,
            - PrefixPath=/mypath # 跟StripPrefix相反,会添加后转发
            - Auth     # 自定义的权限过滤器
            - IPForbid=0:0:0:0:0:0:0:1 #自定义的IP拦截器
            - ResponseBody  #自定义的响应结果拦截器
3、启动类设置为Eureka的客户端
@EnableDiscoveryClient
@SpringBootApplication
public class GateWayApplication {
	public static void main(String[] args) {
		SpringApplication.run(GateWayApplication.class, args);
	}
}
四、使用Zull网关中心
1、添加配置项
zuul:  # zuul配置
  ignoredServices: zuul-server  
  routes:
    microservice-provider-user: /user/**  
2、添加配置类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableZuulProxy
@EnableEurekaClient
@SpringCloudApplication
public class ApiGatewayMicroApplication {
  public static void main(String[] args) {
    SpringApplication.run(ApiGatewayMicroApplication.class, args);
  }
}
1、自定义拦截器

Zull有请求过滤的功能,其过滤器可以在Http请求的发起和响应返回期间执行一系列的过滤器。Zuul包扩以下四种过滤器:

  • PRE: 该类型的filters在Request routing到源web-service之前执行。可以进行一些权限认证,日志记录,或者额外给Request增加一些属性供后续的filter使用;
  • ROUTING:该类型的filters用于把Request routing到源web-service,源web-service是实现业务逻辑的服务。这里使用HttpClient请求web-service;
  • POST:该类型的filters在ROUTING返回Response后执行。用来实现对Response结果进行修改,收集统计数据以及把Response传输会客户端;
  • ERROR:上面三个过程中任何一个出现错误都交由ERROR类型的filters进行处理。

Zuul过滤器具有以下关键特性:

  • Type(类型):Zuul过滤器的类型,这个类型决定过滤器在哪个阶段执行,例如:pre,post等阶段;
  • Execution Order(执行顺序):规定了过滤器的执行顺序,Order的值越小,越先执行;
  • Criteria(标准):Filters执行所需条件
  • Action(行动):如果符合执行条件,则执行Action(具体逻辑代码)
(1)定义前置拦截器
public class PreZuulFilter extends ZuulFilter {
  private static final Logger LOGGER = LoggerFactory.getLogger(PreZuulFilter.class);
 
  @Override
  public Object run() {
    //获取请求内容
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();
    String param = request.getParameter("XXXX");
    //TODO  这里可以进行参数验签解密,权限验证,日志记录,或者额外提供一些信息
    String host = request.getRemoteHost();
    PreZuulFilter.LOGGER.info("请求的host:{}", host);
    return null;
  }
  @Override
  public boolean shouldFilter() {
    return true;
  }
  @Override
  public String filterType() {
    return "pre"; //定义拦截器类型
  }
  @Override
  public int filterOrder() {
    return 0;
  }
}
(3)、鉴权拦截器
@Configuration
public class ApiGatewayFilter extends ZuulFilter {
  @Override
  public String filterType() {
    return "pre";
  }

  @Override
  public int filterOrder() {
    return 0;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() throws ZuulException {
    RequestContext context = RequestContext.getCurrentContext();
    HttpServletRequest request = context.getRequest();
    Principal principal = request.getUserPrincipal();
    //获取用户的登录id
    String userId = principal.getName();
    context.addZuulRequestHeader("X-AUTH-ID",userId);
    return null;
  }
}
(4)配置类中增加拦截器
@Bean
public PreZuulFilter preZuulFilter() {
	return new PreZuulFilter();
}
2、设置熔断
MyFallbackProvider 实现ZuulFallbackProvider接口
@Component
public class CustomerFallbackProvider implements ZuulFallbackProvider {

  @Override
  public String getRoute() {
    return "*";//设置路由,可以是所有的服务或者指定服务
  }

  @Override
  public ClientHttpResponse fallbackResponse() {
    return new ClientHttpResponse() {
      @Override
      public HttpStatus getStatusCode() throws IOException {
        return HttpStatus.BAD_REQUEST;
      }

      @Override
      public int getRawStatusCode() throws IOException {
        return HttpStatus.BAD_REQUEST.value();
      }

      @Override
      public String getStatusText() throws IOException {
        return HttpStatus.BAD_REQUEST.getReasonPhrase();
      }

      @Override
      public void close() {
      }

      @Override
      public InputStream getBody() throws IOException {
        return new ByteArrayInputStream(("fallback" + 
                    MyFallbackProvider.this.getRoute()).getBytes());
      }

      @Override
      public HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
      }
    };
  }
}
3、使用正则表达式处理,启动类添加PatternServiceRouteMapper
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
  public static void main(String[] args) {
    SpringApplication.run(ZuulApplication.class, args);
  }

  @Bean
  public PatternServiceRouteMapper serviceRouteMapper() {
    return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
  }
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值