首页 文章 精选 留言 我的

精选列表

搜索[自动化测试平台],共10000篇文章
优秀的个人博客,低调大师

选型宝访谈:如何打造云时代的一体化供应链平台?

写在前面 供应链是现代企业竞争的焦点,线上拼用户和流量,线下拼供应链,已成为许多企业的共识。近年来,在“中国制造”和“电子商务”大发展的带动下,SCM已成为许多企业继ERP之后的新标配。 今天,移动互联网蓬勃发展,云计算、大数据、AI等技术日臻成熟,企业信息系统云端化成为趋势,新技术应用成为常态。在这样的背景下,企业传统的SCM系统该何去何从,成为很多CIO朋友非常关心的话题。 李维良(主持人) 请您先为大家介绍一下,什么是供应链(Supply Chain)和供应链管理?供应链通常包含哪些节点和主体? 许效军(嘉宾) 关于供应链和供应链管理,在很多教科书上,有很多专业的说法。通俗一点来讲,供应链就是围绕核心企业,通过对物流、信息流、资金流的控制,构建的从采购原材料开始,到制成中间产品及最终产品、最后由销售网络把产品送到消费者手中的一个网链。在这个网链中,通常会包含供应商、经销商、物流服务商、最终客户等众多主体。 可以说,供应链是一个企业从端到端,贯穿经营全过程的一个重要职能,而供应链管理就是对整个供应链系统进行计划、协调、操作、控制和优化的各种活动和过程。为供应链管理提供支撑的信息化系统,就是我们常说的SCM(供应链管理系统 Supply Chain Management)。 李维良 进入新时代,为什么传统的SCM需要转型?转型的方向是什么? 许效军 随着经济全球化、互联网化的不断推进,企业的供应链系统也在发生着巨大的变化,比如:供应链进一步拉长、客户订单日益多样化、交货期越来越短、仓库及承运商多而分散等等。竞争的不断加剧、新商业模式的不断出现,使市场变得越来越不可预测,波动成为一种新的常态。 在这样的背景下,传统SCM系统的弊端开始显现:订单、仓储、运输等系统各自为政,缺乏信息共享与协同机制,难以应付激增的海量数据,无法对市场变化做出及时的响应和调整……传统的供应链管理系统越来越力不从心,变革势在必行。 与此同时,移动互联网、云计算、大数据、物联网、人工智能等新技术日益成熟,应用的成本越来越低,这也为供应链管理系统的变革与创新提供了可能。 供应链具有先天的多连接属性,从外部的供应商、经销商到内部的销售、采购、生产、仓储、运输,从货车司机到终端消费者,都是供应链连接的对象。移动+社交、云计算、物联网等技术的普及,使今天的供应链系统可以将这些对象更好地连接起来。传统的物流、资金流、信息流“三流合一”的理念需要升级,今天,通过新技术的应用,可以将供应链中的第四大要素“人”也连接起来,做到“四流合一”。 连接自然会带来海量的数据,而大数据技术可以帮助我们从海量数据中获取价值,从而进一步优化供应链系统。 经过几年的发展和应用,云计算培养了C端市场“On Demand”也就是“按需服务”的能力和习惯,今天,这种消费习惯也延伸到了B端。 不难看出,移动化、云端化、一体化、智能化,都是供应链系统转型的重要方向。新一代SCM正依托新技术,连接包括人在内的更多要素,实现供应链数据的无缝流转,为企业打造应需而变、智慧决策、灵活扩展、快速响应的数字化供应链系统。

优秀的个人博客,低调大师

java版spring cloud+spring boot 社交电子商务平台-Ribbon设计原理

一. Spring集成下的Ribbon工作结构 先贴一张总览图,说明一下Spring如何集成Ribbon的,如下所示: Spring Cloud集成模式下的Ribbon有以下几个特征: 1.Ribbon 服务配置方式 每一个服务配置都有一个Spring ApplicationContext上下文,用于加载各自服务的实例。 和Feign的集成模式 在使用Feign作为客户端时,最终请求会转发成 http://<服务名称>/的格式,通过LoadBalancerFeignClient, 提取出服务标识<服务名称>,然后根据服务名称在上下文中查找对应服务的负载均衡器FeignLoadBalancer,负载均衡器负责根据既有的服务实例的统计信息,挑选出最合适的服务实例。 二、Spring Cloud模式下和Feign的集成实现方式 和Feign结合的场景下,Feign的调用会被包装成调用请求LoadBalancerCommand,然后底层通过Rxjava基于事件的编码风格,发送请求;Spring Cloud框架通过 Feigin 请求的URL,提取出服务名称,然后在上下文中找到对应服务的的负载均衡器实现FeignLoadBalancer,然后通过负载均衡器中挑选一个合适的Server实例,然后将调用请求转发到该Server实例上,完成调用,在此过程中,记录对应Server实例的调用统计信息。 /** * Create an {@link Observable} that once subscribed execute network call asynchronously with a server chosen by load balancer. * If there are any errors that are indicated as retriable by the {@link RetryHandler}, they will be consumed internally by the * function and will not be observed by the {@link Observer} subscribed to the returned {@link Observable}. If number of retries has * exceeds the maximal allowed, a final error will be emitted by the returned {@link Observable}. Otherwise, the first successful * result during execution and retries will be emitted. */ public Observable<T> submit(final ServerOperation<T> operation) { final ExecutionInfoContext context = new ExecutionInfoContext(); if (listenerInvoker != null) { try { listenerInvoker.onExecutionStart(); } catch (AbortExecutionException e) { return Observable.error(e); } } // 同一Server最大尝试次数 final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer(); //下一Server最大尝试次数 final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer(); // Use the load balancer // 使用负载均衡器,挑选出合适的Server,然后执行Server请求,将请求的数据和行为整合到ServerStats中 Observable<T> o = (server == null ? selectServer() : Observable.just(server)) .concatMap(new Func1<Server, Observable<T>>() { @Override // Called for each server being selected public Observable<T> call(Server server) { // 获取Server的统计值 context.setServer(server); final ServerStats stats = loadBalancerContext.getServerStats(server); // Called for each attempt and retry 服务调用 Observable<T> o = Observable .just(server) .concatMap(new Func1<Server, Observable<T>>() { @Override public Observable<T> call(final Server server) { context.incAttemptCount();//重试计数 loadBalancerContext.noteOpenConnection(stats);//链接统计 if (listenerInvoker != null) { try { listenerInvoker.onStartWithServer(context.toExecutionInfo()); } catch (AbortExecutionException e) { return Observable.error(e); } } //执行监控器,记录执行时间 final Stopwatch tracer = loadBalancerContext.getExecuteTracer().start(); //找到合适的server后,开始执行请求 //底层调用有结果后,做消息处理 return operation.call(server).doOnEach(new Observer<T>() { private T entity; @Override public void onCompleted() { recordStats(tracer, stats, entity, null); // 记录统计信息 } @Override public void onError(Throwable e) { recordStats(tracer, stats, null, e);//记录异常信息 logger.debug("Got error {} when executed on server {}", e, server); if (listenerInvoker != null) { listenerInvoker.onExceptionWithServer(e, context.toExecutionInfo()); } } @Override public void onNext(T entity) { this.entity = entity;//返回结果值 if (listenerInvoker != null) { listenerInvoker.onExecutionSuccess(entity, context.toExecutionInfo()); } } private void recordStats(Stopwatch tracer, ServerStats stats, Object entity, Throwable exception) { tracer.stop();//结束计时 //标记请求结束,更新统计信息 loadBalancerContext.noteRequestCompletion(stats, entity, exception, tracer.getDuration(TimeUnit.MILLISECONDS), retryHandler); } }); } }); //如果失败,根据重试策略触发重试逻辑 // 使用observable 做重试逻辑,根据predicate 做逻辑判断,这里做 if (maxRetrysSame > 0) o = o.retry(retryPolicy(maxRetrysSame, true)); return o; } }); // next请求处理,基于重试器操作 if (maxRetrysNext > 0 && server == null) o = o.retry(retryPolicy(maxRetrysNext, false)); return o.onErrorResumeNext(new Func1<Throwable, Observable<T>>() { @Override public Observable<T> call(Throwable e) { if (context.getAttemptCount() > 0) { if (maxRetrysNext > 0 && context.getServerAttemptCount() == (maxRetrysNext + 1)) { e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED, "Number of retries on next server exceeded max " + maxRetrysNext + " retries, while making a call for: " + context.getServer(), e); } else if (maxRetrysSame > 0 && context.getAttemptCount() == (maxRetrysSame + 1)) { e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_EXEEDED, "Number of retries exceeded max " + maxRetrysSame + " retries, while making a call for: " + context.getServer(), e); } } if (listenerInvoker != null) { listenerInvoker.onExecutionFailed(e, context.toFinalExecutionInfo()); } return Observable.error(e); } }); } 从一组ServerList 列表中挑选合适的Server /** * Compute the final URI from a partial URI in the request. The following steps are performed: * <ul> * <li> 如果host尚未指定,则从负载均衡器中选定 host/port * <li> 如果host 尚未指定并且尚未找到负载均衡器,则尝试从 虚拟地址中确定host/port * <li> 如果指定了HOST,并且URI的授权部分通过虚拟地址设置,并且存在负载均衡器,则通过负载就均衡器中确定host/port(指定的HOST将会被忽略) * <li> 如果host已指定,但是尚未指定负载均衡器和虚拟地址配置,则使用真实地址作为host * <li> if host is missing but none of the above applies, throws ClientException * </ul> * * @param original Original URI passed from caller */ public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException { String host = null; int port = -1; if (original != null) { host = original.getHost(); } if (original != null) { Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original); port = schemeAndPort.second(); } // Various Supported Cases // The loadbalancer to use and the instances it has is based on how it was registered // In each of these cases, the client might come in using Full Url or Partial URL ILoadBalancer lb = getLoadBalancer(); if (host == null) { // 提供部分URI,缺少HOST情况下 // well we have to just get the right instances from lb - or we fall back if (lb != null){ Server svc = lb.chooseServer(loadBalancerKey);// 使用负载均衡器选择Server if (svc == null){ throw new ClientException(ClientException.ErrorType.GENERAL, "Load balancer does not have available server for client: " + clientName); } //通过负载均衡器选择的结果中选择host host = svc.getHost(); if (host == null){ throw new ClientException(ClientException.ErrorType.GENERAL, "Invalid Server for :" + svc); } logger.debug("{} using LB returned Server: {} for request {}", new Object[]{clientName, svc, original}); return svc; } else { // No Full URL - and we dont have a LoadBalancer registered to // obtain a server // if we have a vipAddress that came with the registration, we // can use that else we // bail out // 通过虚拟地址配置解析出host配置返回 if (vipAddresses != null && vipAddresses.contains(",")) { throw new ClientException( ClientException.ErrorType.GENERAL, "Method is invoked for client " + clientName + " with partial URI of (" + original + ") with no load balancer configured." + " Also, there are multiple vipAddresses and hence no vip address can be chosen" + " to complete this partial uri"); } else if (vipAddresses != null) { try { Pair<String,Integer> hostAndPort = deriveHostAndPortFromVipAddress(vipAddresses); host = hostAndPort.first(); port = hostAndPort.second(); } catch (URISyntaxException e) { throw new ClientException( ClientException.ErrorType.GENERAL, "Method is invoked for client " + clientName + " with partial URI of (" + original + ") with no load balancer configured. " + " Also, the configured/registered vipAddress is unparseable (to determine host and port)"); } } else { throw new ClientException( ClientException.ErrorType.GENERAL, this.clientName + " has no LoadBalancer registered and passed in a partial URL request (with no host:port)." + " Also has no vipAddress registered"); } } } else { // Full URL Case URL中指定了全地址,可能是虚拟地址或者是hostAndPort // This could either be a vipAddress or a hostAndPort or a real DNS // if vipAddress or hostAndPort, we just have to consult the loadbalancer // but if it does not return a server, we should just proceed anyways // and assume its a DNS // For restClients registered using a vipAddress AND executing a request // by passing in the full URL (including host and port), we should only // consult lb IFF the URL passed is registered as vipAddress in Discovery boolean shouldInterpretAsVip = false; if (lb != null) { shouldInterpretAsVip = isVipRecognized(original.getAuthority()); } if (shouldInterpretAsVip) { Server svc = lb.chooseServer(loadBalancerKey); if (svc != null){ host = svc.getHost(); if (host == null){ throw new ClientException(ClientException.ErrorType.GENERAL, "Invalid Server for :" + svc); } logger.debug("using LB returned Server: {} for request: {}", svc, original); return svc; } else { // just fall back as real DNS logger.debug("{}:{} assumed to be a valid VIP address or exists in the DNS", host, port); } } else { // consult LB to obtain vipAddress backed instance given full URL //Full URL execute request - where url!=vipAddress logger.debug("Using full URL passed in by caller (not using load balancer): {}", original); } } // end of creating final URL if (host == null){ throw new ClientException(ClientException.ErrorType.GENERAL,"Request contains no HOST to talk to"); } // just verify that at this point we have a full URL return new Server(host, port); }

优秀的个人博客,低调大师

java B2B2C源码电子商务平台 ---搭建Eureka注册中心

一创建一个SpringBoot工程,命名为eureka-server,并在pom.xml中引入必要的依赖,代码如下。愿意了解源码的朋友直接求求交流分享技术:二一四七七七五六三三 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.7.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-starter-actuator</artifactId>--> <!--</dependency>--> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 二 通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用程序进行对话,只需要在SpringBoot应用中添加下面这个注解就能开启此功能。 @EnableEurekaServer @SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } } 三在默认情况下,服务注册中也会将自己作为客户端来尝试注册它自己,所以需要禁用它的客户端行为。 application.properties中增加如下配置。 spring.application.name=eureka-server server.port=1111 eureka.instance.hostname=localhost # 关闭保护机制 #eureka.server.enable-self-preservation=false eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ logging.file=${spring.application.name}.log 说明:eureka.client.register-with-eureka:由于该应用为注册中心,所以设置为false,代表不向注册中心注册自己。eureka.client.fetch-registry:由于注册中心的职责就是维护服务实例,它并不需要去检索服务,所以也设置为false。整体代码结构如下:资料和源码来源

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册