精华内容
下载资源
问答
  • 燃气轮机经典书籍 尊重版权,仅限学术交流
  • 1、Turbine 2、zuul API网关 3、config配置中心

    一、Turbine

    1、介绍

    Turbine:聚合多台服务器的监控数据,提供给仪表盘进行展现。

    2、搭建Turbine服务

    (1)添加Turbine依赖

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

    (2)yml文件配置

    • 1、聚合的服务id
    • 2、集群名:聚合之后的数据的命名,默认名:defalut
    • 3、添加注解:@EnableTurbine、@EnableDiscoveryClient(可省略)
    • 4、监控路径:
    • http://localhost:4001/hystrix
    • http://localhost:5001/trubine.stream
    turbine:
      # 集合的服务id
      app-config: order-service
      # 给聚合的数据命名
      cluster-name-expression: new String("default")
    

    hystrix dashboard 一次只能监控一个服务实例,使用 turbine 可以汇集监控信息,将聚合后的信息提供给 hystrix dashboard 来集中展示和监控

    二、zuul ZPI网关

    1、API网关

    为微服务应用提供统一的调用入口,简化客户端的访问。

    在微服务架构中,通常会有多个服务提供者。设想一个电商系统,可能会有商品、订单、支付、用户等多个类型的服务,而每个类型的服务数量也会随着整个系统体量的增大也会随之增长和变更。作为UI端,在展示页面时可能需要从多个微服务中聚合数据,而且服务的划分位置结构可能会有所改变。网关就可以对外暴露聚合API,屏蔽内部微服务的微小变动,保持整个系统的稳定性。

    当然这只是网关众多功能中的一部分,它还可以做负载均衡,统一鉴权,协议转换,监控监测等一系列功能。

    2、zuul

    Zuul是Spring Cloud全家桶中的微服务API网关。

    所有从设备或网站来的请求都会经过Zuul到达后端的Netflix应用程序。作为一个边界性质的应用程序,Zuul提供了动态路由、监控、弹性负载和安全功能。Zuul底层利用各种filter实现如下功能:

    • 认证和安全 识别每个需要认证的资源,拒绝不符合要求的请求。
    • 性能监测 在服务边界追踪并统计数据,提供精确的生产视图。
    • 动态路由 根据需要将请求动态路由到后端集群。
    • 压力测试 逐渐增加对集群的流量以了解其性能。
    • 负载卸载 预先为每种类型的请求分配容量,当请求超过容量时自动丢弃。
    • 静态资源处理 直接在边界返回某些响应。

    3、zuul API网关

    在这里插入图片描述

    • 统一的调用入口,简化客户端访问,代理转发
    • 统一的权限校验。eg:登录访问权限
    • 集成ribbon:负载均衡、重试
    • 集成Hystrix:降级、熔断、调用容错

    3.1、统一调用入口

    (1)添加zuul依赖

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

    (2)yml配置转发规则

    zuul 路由配置可以省略,缺省以服务 id 作为访问路径

    zuul:
      routes:
      # 服务id    :  路径的映射规则 
        item-service: /item-service/** //任何访问
        user-service: /user-service/**
        order-service: /order-service/**
    

    zuul的默认规则:自动配置,serviceId直接映射成一个子路径
    可以根据注册表信息自动配置这个默认规则,刚启动只配置一次,内容不全,尽量不使用自动配置,为了防止注册信息不全,最好手动配置。

    (3)主启动类加注解:@EnableZuulProxy@EnableDiscoveryClient(可省略)

    @EnableZuulProxy:代理
    多个zuul服务器集群
    Nginx前端F5硬件,处理服务器X引擎

    3.2、统一权限校验

    (1)、zuul请求过滤

    使用zuul过滤器实现权限验证
    eg:登录权限—>?token=。。。有token则已登录,可访问
    过滤器:pre(过滤器)、routing(路由过滤器)、post(后置过滤器)、custom(自定义过滤器)、origin(开源过滤器)、error(错误过滤器)
    在这里插入图片描述

    添加过滤器

    (1)只需要添加一个zuul过滤器子类,添加@Component注解,没有任何配置,zuul可以自动配置
    (2)继承zuulProxy父类:ZuulFilter
    (3)实现抽象方法

    package cn.tedu.sp11.filter;
    
    import cn.tedu.web.util.JsonResult;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Component
    public class AccessFilter extends ZuulFilter {
        //过滤器类型:pre, routes, post, error
        @Override
        public String filterType() {
    //        return "pre";
            return FilterConstants.PRE_TYPE;//配置成前置过滤器
        }
    
        //当前过滤器添加到哪个位置,返回一个序列号
         //该过滤器顺序要 > 5,才能得到 serviceid
        @Override
        public int filterOrder() {
            /**
             * 前置过滤器其中已经存在5个默认的过滤器,
             * 在第5个过滤器中,向上下文对象中添加了“service-id”属性
             * 要访问service-id必须放在第5个过滤器之后,否则会访问不到
             */
            return 6;
        }
    
        //针对当前请求进行判断,是否执行过滤代码(run方法)
        //对指定的serviceid过滤,如果要过滤所有服务,直接返回 true
        @Override
        public boolean shouldFilter() {
            //判断当前请求调用的是否是item-service
            //如果请求item,执行过滤代码,否则跳过过滤代码
            //需要获得正在调用的服务id
            //zuul请求上下文对象
            RequestContext requestContext = RequestContext.getCurrentContext();
            //从上下文对象获取“服务id”属性的值
            String serviceId = (String) requestContext.get(FilterConstants.SERVICE_ID_KEY);
            return "item-service".equalsIgnoreCase(serviceId);//忽略大小写比较
        }
    
        //过滤方法,权限判断写在这里
        @Override
        public Object run() throws ZuulException {
            //判断是否有token对象
            //获得request
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest request = requestContext.getRequest();
            //用request接收token参数
            String token = request.getParameter("token");
            //判断token参数是否为空,null,“”,“    ”,阻止继续访问,返回登录提示
            if(StringUtils.isBlank(token)){
                //判断null,空串,空包字符串
                此设置会阻止请求被路由到后台微服务
                requestContext.setSendZuulResponse(false);//阻止继续访问
                //JsonResult-->“{code:400,msg:not log in,data:null}”
                String json = JsonResult.err().code(JsonResult.NOT_LOGIN).msg("Not Log In 未登录").toString();
                //向客户端的响应,返回状态码
                requestContext.setResponseStatusCode(JsonResult.NOT_LOGIN);
                //中文编码
                requestContext.addZuulResponseHeader("Content-Type","application/json;charset=UTF-8");
                requestContext.setResponseBody(json);
            }
            //zuul过滤器返回的数据设计为以后扩展使用,当前zuul版本中,这个返回值没有使用,不起任何作用
            return null;
        }
    }
    

    (2)、zuul Cookie过滤

    zuul 会过滤敏感 http 协议头,默认过滤以下协议头:

    • Cookie
    • Set-Cookie
    • Authorization

    可以设置 zuul 不过滤这些协议头

    zuul:
      sensitive-headers:
    

    3.3、zuul继承ribbon

    默认启用负载均衡,不启用重试(zuul不推荐启用重试)

    配置启动重试

    (1)添加spring-retry依赖

    <dependency>
    	<groupId>org.springframework.retry</groupId>
    	<artifactId>spring-retry</artifactId>
    </dependency>
    

    (2)yml配置:zuul.retryable=true

    # 需要开启重试,默认不开启
    zuul:
      retryable: true
    

    (3)重试参数默认值:

    • MaxAutoRetries: 0
    • MaxAutoRetriesNextServer: 1
    • ReadTimeout: 1000
    ribbon:
      ReadTimeout: 1000
      MaxAutoRetriesNextServer: 1
      MaxAutoRetries: 0
    

    3.4、zuul继承Hystrix:0配置,默认启用

    添加降级代码:

    (1)实现FallbackProvider接口,降级提供接口,安家口要求,添加降级代码
    (2)@Component:自动配置,来应用降级类
    getRoute() 方法中指定应用此降级类的服务id,星号或null值可以通配所有服务

    package cn.tedu.sp11.fallback;
    
    import cn.tedu.web.util.JsonResult;
    import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.client.ClientHttpResponse;
    import org.springframework.stereotype.Component;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    @Component
    public class ItemFallback implements FallbackProvider {//降级提供接口
    
        /**
         * 返回一个服务id,表示当前降级类是针对哪个服务的降级
         */
        @Override
        public String getRoute() {
            //当执行item-service失败,
    	    //应用当前这个降级类
            return "item-service";//只针对商品进行降级
            //星号和null都表示所有微服务失败都应用当前降级类
    		//"*"; //null;
    //        return "*";//针对所有服务都应用当前降级类
    //        return null;//针对所有服务都应用当前降级类
        }
    
        //向客户端发回的降级响应,封装到request对象
         //ClientHttpResponse中封装降级响应
        @Override
        public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
            return new ClientHttpResponse() {
            	 //下面三个方法都是协议号
                @Override
                public HttpStatus getStatusCode() throws IOException {//协议状态码的封装对象code:200,text:ok
                    return HttpStatus.OK;
                }
    
                @Override
                public int getRawStatusCode() throws IOException {//200
                    return HttpStatus.OK.value();
                }
    
                @Override
                public String getStatusText() throws IOException {//ok
                    return HttpStatus.OK.getReasonPhrase();
                }
    
                @Override
                public void close() {
    
                }
    
                @Override
                public InputStream getBody() throws IOException {//协议体
                    String json = JsonResult.err().msg("调用商品服务失败!").toString();
                    return new ByteArrayInputStream(json.getBytes("UTF-8"));
                }
    
                @Override
                public HttpHeaders getHeaders() {//协议头
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                    return httpHeaders;
                }
            };
        }
    }
    

    暴露hystrix.stream端点

    • (1)actuator依赖(zuul已包含)
    • (2)m.e.w.e.i =htystri.stream
    # 降低hystrix 超时时间,以便测试降级
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 500
    
    # 暴露 hystrix.stream 监控端点
    management:
      endpoints:
        web:
          exposure:
            include: hystrix.stream
    
    • (3)修改Turbine yml文件中聚合的服务添加zuul服务
    turbine:
      app-config: order-service, zuul
      cluster-name-expression: new String("default")
    

    4、Feign和zuul的区别

    同:

    • 向后台服务调用
    • 集成ribbon
    • 集成Hystrix
      异:
    • 业务服务之间调用使用Feign
    • zuul部署在最前面,作为系统入口
    • Feign不推荐使用Hystrix
    • zuul不推荐使用ribbon重试

    4.1、Feign不推荐使用Hystrix?

    Feign在业务服务之间添加Hystrix会造成混乱,难以确定故障点,应该在最前面加入Hystrix,避免混乱。

    4.2、zuul不推荐使用ribbon重试?

    ribbon重试在网关处重试会造成后台多个服务压力翻倍,重试的动作应尽量往后放。

    三、config配置中心

    在这里插入图片描述
    yml 配置文件保存到 git 服务器,例如 github.com 或 gitee.com

    微服务启动时,从服务器获取配置文件

    git---->代码管理,版本控制工具
    三代版本控制工具:

    • CVS
    • SVN
    • GUT—linus开发

    git服务器:

    • github
    • gittee
    • gitlab
    • codechina.csdn.net

    1、本地仓库:把远程仓库传到本地仓库

    将自己的项目代码,分享、提交到本地仓库
    IDEA 可以把任意一个文件夹设置成本地仓库,这个文件夹下所有的文件都可以加入这个本地仓库

    aa
      |- bb
          |- git-test 工程, 本地仓库
              |- demo1 
                  |- ....
    

    2、向远处仓库推送:把本地仓库上传到远程仓库

    3、从远程库拉取

    4、版本冲突

    两个开发者修改同一个文件,会引起修改冲突

    解决冲突 merge:
    1、拉取远程代码
    2、在本地完成代码合并
    3、重新提交推送

    5、分支

    6、克隆:把远程仓库下载到本地,在本地新建一个本地仓库

    7、fork:把别人的仓库,叉到自己的账户下,创建一个仓库副本

    8、发现优秀项目

    • 在git仓库中搜索关键词
    • 活跃用户的收藏项目
    • 网上文章推荐
    • https://github.com/521xueweihan/HelloGitHub
    展开全文
  • 上一篇涨介绍了Hystrix的Dashboard来展示单个...对于老司机而言,Turbine这个单词不会陌生,中文意思为涡轮机,所以顾名思义,用来描述聚合很形象。 (一)创建一个hystrix-turbine监控数据聚合 新建工程后,子pom....

    上一篇涨介绍了Hystrix的Dashboard来展示单个实例的监测数据,但实际情况中,我们会部署N个微服务,那么不可能再用这种方式一个接着一个输入查看,基于这种场景,Hystrix可以利用Turbine实现数据聚合。对于老司机而言,Turbine这个单词不会陌生,中文意思为涡轮机,所以顾名思义,用来描述聚合很形象。

    (一)创建一个hystrix-turbine监控数据聚合
    新建工程后,子pom.xml文件添加以下依赖:

    <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
            </dependency>
        </dependencies>
    

    然后创建一个application启动类(需要添加注解@EnableDiscoveryClient查看注册中心的注册服务):

    package com.ningmeng.turbine;
    
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
    import org.springframework.cloud.netflix.turbine.EnableTurbine;
    
    @EnableTurbine // 开启turbine监控数据聚合
    @EnableHystrixDashboard // 开启hystrix仪表盘
    @EnableDiscoveryClient
    @SpringBootApplication
    public class HystrixTurbineApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HystrixTurbineApplication.class, args);
        }
    }
    
    

    最后配置application.yml文件:

    #工程端口号
    server:
      port: 5002
    
    spring:
      application:
        name: hystrix-turbine #工程名,后面各个服务间调用接口要用到
      rabbitmq:
        host: localhost
        port: 5672
        username: guest
        password: guest
    #eureka注册中心地址
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/ #eureka服务器所在的地址
    
    #聚合的工程  这里演示聚合ribbon-consumer,feign-consumer这两个消费工程
    turbine:
      aggregator:
        cluster-config: default
      app-config: ribbon-consumer,feign-consumer
      cluster-name-expression: new String("default")
    

    注意:new String(“default”)必须这么写,否则启动的时候会抛出异常org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field ‘default‘ cannot be found on object of type ‘com.netflix.appinfo.InstanceInfo‘ - maybe not public or not valid?

    参数说明:
    turbine.aggregator.cluster-config参数设定cluster名字,当使用default,默认聚合turbine.appConfig中设定的所有服务名的数据;
    turbine.app-config参数设定需要收集监控信息的服务名;
    turbine.cluster-name-expression 参数指定了集群名称为 default,当我们服务数量非常多的时候,可以启动多个 Turbine 服务来构建不同的聚合集群,而该参数可以用来区分这些不同的聚合集群,同时该参数值可以在 Hystrix 仪表盘中用来定位不同的聚合集群,只需要在 Hystrix Stream 的 URL 中通过 cluster 参数来指定。

    最后依次启动eureka-server、service-provider(两个实例)、feign-consumer、ribbon-consumer以及hystrix-turbine工程,然后访问http://localhost:5002/hystrix,可看到页面:
    在这里插入图片描述
    我们能通过红色框中的两种方式查看监控聚合数据(注意与bashboard监控面板的区别,turbine拼接的是turbine-hostname:port(即hystrix-turbine的ip以及端口号),bashboard拼接的是hystrix-app:port(即调用服务的消费工程的ip以及端口号,比如ribbon-consumer或者feign-consumer))

    第一种方式:直接输入http://localhost:5002/turbine.stream,然后假如处于loading状态,我们先调用服务,之后能看到以下内容:
    在这里插入图片描述

    第二种方式在输入url后拼接cluster(即turbine.aggregator.cluster-config的值):
    修改application.yml配置文件:

    #工程端口号
    server:
      port: 5002
    
    spring:
      application:
        name: hystrix-turbine #工程名,后面各个服务间调用接口要用到
      rabbitmq:
        host: localhost
        port: 5672
        username: guest
        password: guest
    #eureka注册中心地址
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/ #eureka服务器所在的地址
    
    #聚合的工程  这里演示聚合ribbon-consumer,feign-consumer这两个消费工程
    turbine:
      aggregator:
        cluster-config: FEIGN-CONSUMER
      app-config: feign-consumer
    #  cluster-name-expression: new String("default")
    

    参数说明:cluster-config配置的名称必须为eureka注册中心上已经注册的服务名,且为大写。

    然后访问http://localhost:5002/turbine.stream?cluster=FEIGN-CONSUMER,cluster参数必须与turbine.aggregator.cluster-config中的条目匹配,只能看到feign-consumer服务的监控数据:
    在这里插入图片描述

    本篇完结!

    github代码地址:https://github.com/huijunzeng/springCloudDemo.git

    展开全文
  • Turbine 框架介绍

    2011-11-26 00:16:32
    Jakarta Turbine 可能大家不是非常熟悉,但是它是一个很好的Web Framework(也有缺点,在以后为大家指出),先介绍一下 Turbine 的整体结构吧。 Turbine 的开发包叫:TDK(Turbine Developer Kit),它有一组Jakarta ...

    Jakarta Turbine 可能大家不是非常熟悉,但是它是一个很好的Web Framework(也有缺点,在以后为大家指出),先介绍一下 Turbine 的整体结构吧。 
    Turbine 的开发包叫:TDK(Turbine Developer Kit),它有一组Jakarta Turbine子项目组成(Turbine 项目的子项目数仅次于 Jakarta Common 项目位于第二位),列举如下: 
    1.Turbine:核心框架 
    2.Torque:JDO 的Turbine实现,利用XML技术将关系性数据库和Java Class OO 映射,足以让你耳目一新。 
    3.Fulcrum:服务框架,提供了大量的Web系统服务功能,个人认为Intake Service,Cache Service很棒。 
    4.Maven:Java 项目更新工具,基于POM(project object model)概念,目前只支持Turbine。 
    5.JCS(Java Caching System):是服务器段Java程序的分布式缓存系统,极棒的概念。 
    6.Stratum:Turbine 和 Avalon 的移植工具。 
    还有一个和 Turbine 轻密如战友的 Web 模版项目 Jakarta Velocity. 

    1.Turbine 的历史: 

    我用Turbine的时候是 2001年1月(很早吧),当时的 TDK 版本是1.1.A13,非常的不稳定,并且有相当多的 Bug.当时促使我们用Turbine的原因是其良好的MVC架构.当时EJB还根本没有火起来,Struts也只是一个雏形而已,而当时的Turbine已经发展的不错了(当然还有很多的问题). 
    我当时选择了Turbine来开发一套物流管理系统,遇到的最大的困难是帮助文档奇缺,这是Microsoft的开发组件远远胜于当时的Java的一点(注1). 
    但是我觉得当时的Turbine News Group的氛围非常的好,基本上问题都能得到解决,而且Turbine的升级速度要比现在快的多(我觉的最近的Turbine组有点懒,TDK(Turbine)3.0 Released 版本都快等了一年了,我的天).虽然文档很缺(注2),但是有这个新闻组的帮助,项目做的还比较顺利,一个附带的好处是让我习惯了看E文文档. 
    Turbine 的发起人之一是Brett McLaughlin,我比较崇敬的程序员之一(我顶礼膜拜的是伟大的Anders).Turbine的开发宗旨是:"Turbine is a servlet based framework that allows experienced Java developers to quickly build secure web applications."(Turbine 提供给有经验的Java开发者一个快速开发安全Web应用的Servlet平台),我深有感触的觉得的确如此. 
    日志: 
    2000/9 Turbine 诞生了 
    2001/3 Turbine 1.1 Released 
    2001/6 Turbine 2.1 Released 
    2002/12 Turbine 2.2 Released 
    期待 3.0 

    2.一些看法 

    我现在在做 EJB + Struts 的开发,结合现在的经验谈谈我作为第一线的软件设计开发人员的想法. 
    EJB 吸引大家的是它的标准中的容器(Container),组件(Component),分布式概念.我说一句实话,EJB 真的只适合于做大项目(ERP,SCM,CRM 等等),如果你只是做一个 Web Application(1000个人使用,提供100个动态页面的话)真的没必要自己给自己找麻烦. 
    EJB 和数据层的结合用的是 CMP (EJB 2.0 & 2.1 建议尽量使用CMP来取代BMP以提高性能),但是你要用EJB进行抽象、多态是在是太困难了.IBM DeveloperWorks 上的一句话是我感同身受:"CMP 是开发针对于意志薄弱的开发者来说是不适合的". 
    而 Turbine 的 Torque OM 机制使得你能充分发挥你的想象力和创造力,我一直认为计算机语言有好坏之分,标准就是它是否能使你的想象得到实现并且十分漂亮.Turbine Framework 对于开发者来说是十分优秀的. 
    (注3) 

    3.TDK 的相关技术 

    Ant:Turbine 项目的编译技术使用的都是Ant.熟悉Ant至关重要.TDK 的核心编译文件是..\webapps\yourproject\WEB-INF\build\build.xml 和 build.properties.熟悉Ant的人一看,拷,太熟悉了!!! 

    Log4j:Turbine 项目的日志纪录用的是 Jakarta 组的Log4j. 
    --------------------------------------------------------------------------------- 
    # ------------------------------------------------------------------- 

    # L O G G I N G 

    # ------------------------------------------------------------------- 
    # We use Log4J for all Turbine logging and we embed the log4j 
    # properties within our application configuration. 

    # NOTE: 
    # The presence of ${webapp} in the logging configuration 
    # is not a mistake. Internally the value of ${webapp} 
    # is set so that you can use it with standard log4j 
    # properties to get logs to appear in your 
    # webapp space. 
    # ------------------------------------------------------------------- 
    log4j.category.default = INFO, default 
    log4j.appender.default = org.apache.log4j.FileAppender 
    log4j.appender.default.file = ${webapp}/logs/turbine.log 
    log4j.appender.default.layout = org.apache.log4j.PatternLayout 
    log4j.appender.default.layout.conversionPattern = %d [%t] %-5p %c - %m%n 
    log4j.appender.default.append = false 

    log4j.category.sql = DEBUG, sql 
    log4j.appender.sql = org.apache.log4j.FileAppender 
    log4j.appender.sql.file = ${webapp}/logs/sql.log 
    log4j.appender.sql.layout = org.apache.log4j.PatternLayout 
    log4j.appender.sql.layout.conversionPattern = %d [%t] %-5p %c - %m%n 
    log4j.appender.sql.append = false 

    log4j.category.vdebug = DEBUG, vdebug 
    log4j.appender.vdebug = org.apache.log4j.FileAppender 
    log4j.appender.vdebug.file = ${webapp}/logs/vlog.log 
    log4j.appender.vdebug.layout = org.apache.log4j.PatternLayout 
    log4j.appender.vdebug.layout.conversionPattern = %d [%t] %-5p %c - %m%n 
    log4j.appender.vdebug.append = false 
    --------------------------------------------------------------------------------- 
    以上的配置是位于../WEB-INF/conf/下的TurbineResources.properties文件中,看看文件名就知道他有多重要了,我会在以后的文章中详细介绍这个文件的.:-) 

    Velocity:Jakarta 项目组极棒的模版项目,它的配置信息也在TurbineResources.properties中 
    --------------------------------------------------------------------------------- 
    # ------------------------------------------------------------------- 

    # V E L O C I T Y S E R V I C E 

    # ------------------------------------------------------------------- 

    # The location of Velocity configuration file, relative to webapp root 
    # These properties will override the default properties set by Velocity. 
    # You should specify the path to the templates directories as well as 
    # the path to the log file and they should also be relative to webapp root 

    services.VelocityService.template.extension=vm 
    services.VelocityService.default.page.template = /Default.vm 
    services.VelocityService.default.layout.template = /Default.vm 
    services.VelocityService.runtime.log=/logs/velocity.log 
    # 注意定义为中文 GB2312 
    services.VelocityService.input.encoding=GB2312 
    services.VelocityService.velocimacro.library = GlobalMacros.vm 
    services.VelocityService.resource.loader = file 
    services.VelocityService.file.resource.loader.deSCRIPTion = Velocity File Resource Loader 
    services.VelocityService.file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader 
    services.VelocityService.file.resource.loader.path = /templates 
    services.VelocityService.file.resource.loader.cache = false 
    services.VelocityService.file.resource.loader.modificationCheckInterval = 2 
    services.VelocityService.resource.loader = classpath 
    services.VelocityService.classpath.resource.loader.deSCRIPTion = Velocity Classpath Resource Loader 
    services.VelocityService.classpath.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader 

    services.VelocityService.earlyInit = true 
    --------------------------------------------------------------------------------- 
    Velocity 使用的详细介绍可参看我转贴的<>,这篇文章我很早就收集了,实在忘了是那位高人之作(如果是你,请指正,我也真的感谢你的文章对我的帮助). 

    4.结束语 

    这篇文章是我随性写的,看官如果有任何意见的话写信给我:jacky.li@trade-chic.com,绝对虚心接受. 

    注: 
    1.在Java开发之前,我用的是Microsoft Visual Studio 5.0 - 6.0,一个遗留问题是当时写Turbine时的版本控制软件选择的还是SourceSafe 6.0.:-). 
    2.目前为止,我还没有看见过介绍Jakarta组项目的中文书籍,除了一本Velocity的使用手册,我大致看了一看,基本上属于翻译Velocity的Help文档,价值不是很高,没有涉及到Velocity的源代码分析和实现. 
    3.关于 EJB 和 Trubine 技术的比较我会在以后写专题的

    展开全文
  • Turbine 是 apache 项目中的 server-side java 技术,位于 jakarta 子项目 , 是基于 servlet 的 web 应用框架 . 它提供了很多基础服务 : 访问控制 , 页面个性化 , 服务调度 , 表单确认 ,xml-rpc 格式的 web 服务...

    Turbineapache 项目中的server-side java技术,位于jakarta子项目,是基于servletweb应用框架.它提供了很多基础服务:访问控制,页面个性化,服务调度,表单确认,xml-rpc格式的web服务等等.可以做为开发面向服务架构应用的基础,因为turbine很容易开发其它服务,并在其服务管理框架下运行.其下一个版本为2.4,它明确使用亚瑟王神剑项目(该项目实现了IOC模式)来管理服务组件。

     

     

    本文描述的场景是:turbine2.31+velocity-1.4+torque-3.1。要解决在该场景下提交编码模式为multipart/form-data的表单不能正确处理中文的问题,虽然application/x-www-form-urlencoded编码下可以正确处理中文.

     

     

    纠正本问题需要提供:Maven,Ant两个工具以及Turbine源码.

     

     

    首先,Turbine的入口servlet Turbine.java中的代码片段,doGet方法:

    目的是看原始的web服务器请求对象(request)是如何被传递入Turbine中的,以及Turbine如何处理request对象.

        public final void doGet(HttpServletRequest req, HttpServletResponse res)

                throws IOException, ServletException

        {

            // set to true if the request is to be redirected by the page

            boolean requestRedirected = false;

     

     

            // Placeholder for the RunData object.

            RunData data = null;

            try

            {

                // Check to make sure that we started up properly.

                if (initFailure != null)

                {

                    throw initFailure;

                }

     

     

                // Get general RunData here...

                // Perform turbine specific initialization below.

     

     

                data = rundataService.getRunData(req, res, getServletConfig());

     

     

                // If this is the first invocation, perform some

                // initialization.  Certain services need RunData to initialize

                // themselves.

                if (firstDoGet)

                {

                    init(data);

                }

     

     

                // set the session timeout if specified in turbine's properties

                // file if this is a new session

                if (data.getSession().isNew())

                {

                    int timeout = configuration.getInt(SESSION_TIMEOUT_KEY,

                                                       SESSION_TIMEOUT_DEFAULT);

     

     

                    if (timeout != SESSION_TIMEOUT_DEFAULT)

                    {

                        data.getSession().setMaxInactiveInterval(timeout);

                    }

                }

     

     

                // Fill in the screen and action variables.

                data.setScreen(data.getParameters().getString(URIConstants.CGI_SCREEN_PARAM));

                data.setAction(data.getParameters().getString(URIConstants.CGI_ACTION_PARAM));

     

     

                // Special case for login and logout, this must happen before the

                // session validator is executed in order either to allow a user to

                // even login, or to ensure that the session validator gets to

                // mandate its page selection policy for non-logged in users

                // after the logout has taken place.

                if (data.hasAction())

                {

                    String action = data.getAction();

                    log.debug("action = " + action);

     

     

                    if (action.equalsIgnoreCase(

                            configuration.getString(ACTION_LOGIN_KEY,

                                                    ACTION_LOGIN_DEFAULT)))

                    {

                        loginAction(data);

                    }

                    else if (action.equalsIgnoreCase(

                            configuration.getString(ACTION_LOGOUT_KEY,

                                                    ACTION_LOGOUT_DEFAULT)))

                    {

                       logoutAction(data);

                    }

                }

     

     

                // This is where the validation of the Session information

                // is performed if the user has not logged in yet, then

                // the screen is set to be Login. This also handles the

                // case of not having a screen defined by also setting the

                // screen to Login. If you want people to go to another

                // screen other than Login, you need to change that within

                // TurbineResources.properties...screen.homepage; or, you

                // can specify your own SessionValidator action.

                ActionLoader.getInstance().exec(

                        data, configuration.getString(ACTION_SESSION_VALIDATOR_KEY,

                            ACTION_SESSION_VALIDATOR_DEFAULT));

     

     

                // Put the Access Control List into the RunData object, so

                // it is easily available to modules.  It is also placed

                // into the session for serialization.  Modules can null

                // out the ACL to force it to be rebuilt based on more

                // information.

                ActionLoader.getInstance().exec(

                        data, configuration.getString(ACTION_ACCESS_CONTROLLER_KEY,

                            ACTION_ACCESS_CONTROLLER_DEFAULT));

     

     

                // Start the execution phase. DefaultPage will execute the

                // appropriate action as well as get the Layout from the

                // Screen and then execute that. The Layout is then

                // responsible for executing the Navigation and Screen

                // modules.

                //

                // Note that by default, this cannot be overridden from

                // parameters passed in via post/query data. This is for

                // security purposes.  You should really never need more

                // than just the default page.  If you do, add logic to

                // DefaultPage to do what you want.

     

     

                String defaultPage = (templateService == null)

                        ? null :templateService.getDefaultPageName(data);

     

     

                if (defaultPage == null)

                {

                    /*

                     * In this case none of the template services are running.

                     * The application may be using ECS for views, or a

                     * decendent of RawScreen is trying to produce output.

                     * If there is a 'page.default' property in the TR.props

                     * then use that, otherwise return DefaultPage which will

                     * handle ECS view scenerios and RawScreen scenerios. The

                     * app developer can still specify the 'page.default'

                     * if they wish but the DefaultPage should work in

                     * most cases.

                     */

                    defaultPage = configuration.getString(PAGE_DEFAULT_KEY,

                            PAGE_DEFAULT_DEFAULT);

                }

     

     

                PageLoader.getInstance().exec(data, defaultPage);

     

     

                // If a module has set data.acl = null, remove acl from

                // the session.

                if (data.getACL() == null)

                {

                    try

                    {

                        data.getSession().removeAttribute(

                                AccessControlList.SESSION_KEY);

                    }

                    catch (IllegalStateException ignored)

                    {

                    }

                }

     

     

                // handle a redirect request

                requestRedirected = ((data.getRedirectURI() != null)

                                     && (data.getRedirectURI().length() > 0));

                if (requestRedirected)

                {

                    if (data.getResponse().isCommitted())

                    {

                        requestRedirected = false;

                        log.warn("redirect requested, response already committed: " +

                                 data.getRedirectURI());

                    }

                    else

                    {

                        data.getResponse().sendRedirect(data.getRedirectURI());

                    }

                }

     

     

                if (!requestRedirected)

                {

                    try

                    {

                        if (data.isPageSet() == false && data.isOutSet() == false)

                        {

                            throw new Exception("Nothing to output");

                        }

     

     

                        // We are all done! if isPageSet() output that way

                        // otherwise, data.getOut() has already been written

                        // to the data.getOut().close() happens below in the

                        // finally.

                        if (data.isPageSet() && data.isOutSet() == false)

                        {

                            // Modules can override these.

                            data.getResponse().setLocale(data.getLocale());

                            data.getResponse().setContentType(

                                    data.getContentType());

     

     

                            // Set the status code.

                            data.getResponse().setStatus(data.getStatusCode());

                            // Output the Page.

                            data.getPage().output(data.getOut());

                        }

                    }

                    catch (Exception e)

                    {

                        // The output stream was probably closed by the client

                        // end of things ie: the client clicked the Stop

                        // button on the browser, so ignore any errors that

                        // result.

                        log.debug("Output stream closed? ", e);

                    }

                }

            }

            catch (Exception e)

            {

                handleException(data, res, e);

            }

            catch (Throwable t)

            {

                handleException(data, res, t);

            }

            finally

            {

                // Return the used RunData to the factory for recycling.

                rundataService.putRunData(data);

            }

    }

     

     

    以及org.apache.turbine.services.rundata.TurbineRunDataService中的:

    public RunData getRunData(HttpServletRequest req,

                                  HttpServletResponse res,

                                  ServletConfig config)

                throws TurbineException

        {

            return getRunData(DEFAULT_CONFIG, req, res, config);

        }

     

     

        /**

         * Gets a RunData instance from a specific configuration.

         *

         * @param key a configuration key.

         * @param req a servlet request.

         * @param res a servlet response.

         * @param config a servlet config.

         * @return a new or recycled RunData object.

         * @throws TurbineException if the operation fails.

         * @throws IllegalArgumentException if any of the parameters are null.

         */

        public RunData getRunData(String key,

                                  HttpServletRequest req,

                                  HttpServletResponse res,

                                  ServletConfig config)

                throws TurbineException,

                IllegalArgumentException

        {

            // The RunData object caches all the information that is needed for

            // the execution lifetime of a single request. A RunData object

            // is created/recycled for each and every request and is passed

            // to each and every module. Since each thread has its own RunData

            // object, it is not necessary to perform syncronization for

            // the data within this object.

            if ((req == null)

                || (res == null)

                || (config == null))

            {

                throw new IllegalArgumentException("HttpServletRequest, "

                    + "HttpServletResponse or ServletConfig was null.");

            }

     

     

            // Get the specified configuration.

            String[] cfg = (String[]) configurations.get(key);

            if (cfg == null)

            {

                throw new TurbineException("RunTime configuration '" + key + "' is undefined");

            }

     

     

            TurbineRunData data;

            try

            {

                data = (TurbineRunData) pool.getInstance(cfg[0]);

                data.setParameterParser((ParameterParser) pool.getInstance(cfg[1]));

                data.setCookieParser((CookieParser) pool.getInstance(cfg[2]));

            }

            catch (ClassCastException x)

            {

                throw new TurbineException("RunData configuration '" + key + "' is illegal", x);

            }

     

     

            // Set the request and response.

            data.setRequest(req);

            data.setResponse(res);

     

     

            // Set the servlet configuration.

            data.setServletConfig(config);

     

     

            // Set the ServerData.

            data.setServerData(new ServerData(req));

     

     

            return data;

        }

     

     

    org.apache.turbine.services.rundata.DefaultTurbineRunData中的:

        /**

         * Sets the servlet request.

         *

         * @param req a request.

         */

        public void setRequest(HttpServletRequest req)

        {

            this.req = req;

        }

     

     

        /**

         * Sets the servlet response.

         *

         * @param res a response.

         */

        public void setResponse(HttpServletResponse res)

        {

            this.res = res;

        }

     

     

    在该类中request对象被不加修改的传递到了ParameterParser:

        /**

         * Gets the parameters.

         *

         * @return a parameter parser.

         */

        public ParameterParser getParameters()

        {

            // Parse the parameters first, if not yet done.

            if ((this.parameters != null) &&

                    (this.parameters.getRequest() != this.req))

            {

                this.parameters.setRequest(this.req);

            }

            return this.parameters;

        }

     

     

        /**

         * Gets the parameter parser without parsing the parameters.

         *

         * @return the parameter parser.

         */

        public ParameterParser getParameterParser()

        {

            return parameters;

        }

     

     

        /**

         * Sets the parameter parser.

         *

         * @param parser a parameter parser.

         */

        public void setParameterParser(ParameterParser parser)

        {

            parameters = parser;

        }

     

     

    以上类中展示的代码都涉及到了Request对象,从这些代码中看出在Turbine,实际上并没有对Request对象做任何修改,这样就可以按照通常的方法,加入对Request对象的处理代码.最后, 这里是关键的地方:在类DefaultParameterParser中对Request对象的处理,也就是通过对该处的修改,问题得到了解决:

        /**

         * Sets the servlet request to be parser.  This requires a

         * valid HttpServletRequest object.  It will attempt to parse out

         * the GET/POST/PATH_INFO data and store the data into a Map.

         * There are convenience methods for retrieving the data as a

         * number of different datatypes.  The PATH_INFO data must be a

         * URLEncoded() string.

         * <p>

         * To add name/value pairs to this set of parameters, use the

         * <code>add()</code> methods.

         *

         * @param request An HttpServletRequest.

         */

        public void setRequest(HttpServletRequest request)

        {

            clear();

     

     

            uploadData = null;

     

     

            //String enc = request.getCharacterEncoding();

    原来是:

            enc = request.getCharacterEncoding();

    setCharacterEncoding(enc != null ? enc : "US-ASCII");

        修改后:

            if(enc==null)    enc=org.apache.turbine.Turbine.getConfiguration().getString(CharacterEncoding_Key,"US-ASCII");               

            setCharacterEncoding(enc);

            // String object re-use at its best.

            String tmp = null;

     

     

            tmp = request.getHeader("Content-type");

     

     

            if (uploadServiceIsAvailable

                    && uploadService.getAutomatic()

                    && tmp != null

                    && tmp.startsWith("multipart/form-data"))

            {

                log.debug("Running the Turbine Upload Service");

                try

                {

                    TurbineUpload.parseRequest(request, this);

                }

                catch (TurbineException e)

                {

                    log.error("File upload failed", e);

                }

            }

     

     

            for (Enumeration names = request.getParameterNames();

                 names.hasMoreElements();)

            {

                tmp = (String) names.nextElement();

                add(convert(tmp),

                        request.getParameterValues(tmp));

            }

     

     

            // Also cache any pathinfo variables that are passed around as

            // if they are query string data.

            try

            {

                StringTokenizer st =

                        new StringTokenizer(request.getPathInfo(), "/");

                boolean isNameTok = true;

                String pathPart = null;

                while (st.hasMoreTokens())

                {

                    if (isNameTok)

                    {

                        tmp = URLDecoder.decode(st.nextToken());

                        isNameTok = false;

                    }

                    else

                    {

                        pathPart = URLDecoder.decode(st.nextToken());

                        if (tmp.length() > 0)

                        {

                            add(convert(tmp), pathPart);

                        }

                        isNameTok = true;

                    }

                }

            }

            catch (Exception e)

            {

                // If anything goes wrong above, don't worry about it.

                // Chances are that the path info was wrong anyways and

                // things that depend on it being right will fail later

                // and should be caught later.

            }

     

     

            this.request = request;

     

     

            if (log.isDebugEnabled())

            {

                log.debug("Parameters found in the Request:");

                for (Iterator it = keySet().iterator(); it.hasNext();)

                {

                    String key = (String) it.next();

                    log.debug("Key: " + key + " -> " + getString(key));

                }

            }

        }

     

     

    同时在配置文件TurbineResources.properties中加入一项:

    CharacterEncoding=gb2312.可以根据需要修改该值.

     

     

    完成以上修改后,Maven工具重新构建Turbine并得到了新的turbine.jar把它放入你的应用环境中,中文问题即得到正确显示.

       注意:以上代码片段来自Turbine源码.黑体字表示要关注的部分,代码中修改部分则给出了说明.


    原文链接: http://blog.csdn.net/keepeye/article/details/378496

    转载于:https://my.oschina.net/changpinghu/blog/72492

    展开全文
  • 我的springcloudshe版本1.5.8.RELEASE如下: ...turbine 用于整合多个集群的hystrix.stream,通过turbine.stream对外提供整合数据,这样可以监控集群的hystrix.stream调用 http://localhost:po...
  • http://localhost:7090/turbine.stream?cluster=USER-SERVICE http://localhost:7090/turbine.stream?cluster=RECOMMEND-SERVICE http://localhost:7090/hystrix http://...
  • 断路器仪表盘Hystrix Dashboard和Turbine 1.介绍 Hystrix的主要优点之一是它收集关于每个HystrixCommand的一套指标。Hystrix仪表板以有效的方式显示每个断路器的运行状况。 长这个样子,实测对中文并不友好 ...
  • SpringCloud

    千次阅读 2019-11-18 08:54:06
    • Hystrix dashboard,Turbine 负责监控 Hystrix的熔断情况,并给予图形化的展示 • Spring Cloud Confifig 提供了统一的配置中心服务 • 当配置文件发生变化的时候,Spring Cloud Bus 负责通知各服务去获取最新...
  • Turbine实战(下) (转)

    2007-08-16 09:39:53
    Turbine实战(下) (转)[@more@]3.3.2 Layout Layout相当于Screen、Navigation的容器。负责页面的布局控制。XML:namespace prefix = o ns = "urn:s...
  •  hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用了对应的卡通形象做作为logo...
  • QBlade 0.6 多语中文版(桨叶模拟设计工具)是使用叶片元素动量法 (BEM) 和使用多重加倍进行流设计(DMS) 的桨叶设计器,对于垂直轴风力涡轮机(VAWT)或水平轴风力涡轮机 (HAWT) 的集成仿真 是基于软件XFLR5(机翼...
  • Wind Energy Explained

    2011-05-04 17:42:35
    风能权威教材,主要介绍风能相关知识,其中包含了一些经济方面类容
  • Spring Cloud 中文

    2020-08-05 15:30:44
    Spring Cloud 中文网 微服务架构集大成者,云计算最佳业务实践。 Spring Cloud Greenwich中文文档 Spring Cloud Dalston中文文档 Spring Cloud Brixton中文文档 Spring Cloud集成相关优质项目推荐 这些项目是...
  • Spring Cloud 中文文档

    千次阅读 2020-06-10 15:41:22
    Spring Cloud 官方文档 Spring Cloud为开发人员提供了用于快速构建分布式系统中某些常见模式的工具(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调产生了样板模式,并且使用...
  • 15.2. Turbine Stream 16. 客户端负载均衡器: Ribbon 16.1. 怎样引入 Ribbon 16.2. 自定义 Ribbon 客户端 16.3. 自定义所有 Ribbon 客户端的默认配置 16.4. 通过配置属性自定义 Ribbon 客户端 16.5. 在 ...
  • Velocity用户指南旨在帮助页面设计者和内容提供者了解Velocity和其简单而又强大的脚本语言(Velocity Template Language (VTL))。...Velocity+Turbine 方案提供的模板服务将允许web 应用按真正的mvc模式进行开发。
  • Spring Cloud Consul 1.2.0.RELEASE 该项目通过自动配置并绑定到Spring环境和其他Spring编程模型成语,为Spring Boot应用程序提供Consul集成。通过几个简单的注释,您可以快速启用和配置应用程序中的常见模式,并...
  • Spring Cloud Dalston.RELEASE中文文档 Spring Cloud 目录 特性 云原生应用程序 Spring Cloud上下文:应用程序上下文服务 引导应用程序上下文 应用程序上下文层次结构 改变引导位置Properties 覆盖远程...
  • springcloud中文手册API

    千次阅读 2018-10-12 23:28:52
    使用Turbine和Consul Hystrix指标聚合 Spring Cloud Zookeeper 安装Zookeeper 服务发现与Zookeeper 如何激活 注册Zookeeper 使用DiscoveryClient 使用Spring Cloud Zookeeper与...
  • SpringCloud入门

    2019-11-20 16:43:15
    6、服务监控(单机Dashboard与集群turbine),方便运维人员查看微服务架构项目运行时,各个服务器的运行状态 7、服务配置中心(springcloud config),用来通过github统一管理各个微服务的配置文件(yml文件) ...
  • Spring Cloud Netflix中文文档翻译笔记

    万次阅读 2016-11-24 12:38:30
    原文文档地址:http://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.2.2.RELEASE/Spring Cloud Netflix1.2.2.RELEASESpring Cloude Netflix这个项目提供Netflix OSS和Spring Boot apps集成,通过...
  • Velocity是什么? Velocity是一个基于java的...Velocity也可以为Turbine web开发架构提供模板服务(template service)。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。
  • Spring Cloud Consul 中文参考手册 Spring Cloud Consul 1.2.0.RELEASE 该项目通过自动配置并绑定到Spring环境和其他Spring编程模型成语,为Spring Boot应用程序提供Consul集成。通过几个简单的注释,您可以快速...
  • SpringCloud 开发与部署概要

    万次阅读 2018-07-25 09:06:25
    Turbine监控服务间的调用和熔断相关指标 1.4 SpringCloud 与 Dubbo/Dubbox 的比较  与 Dubbo 相比,SpringCloud 提供了完整的微服务各个组件(核心仍然是服务治理),提供更灵活的 REST API 调用方式...
  • 程序内嵌的中文,我们在程序里直接书写中文字符串。 文本文件,利用 FileInputStream 读入文件内容并转换成字符。 XML 文件,利用 XML 解析器读入内存。 数据库,利用 SQL 查询,取得的结果...
  • 本章从实际的中文问题中,分析问题的根本原因,以及解决之道。 注意,本章虽然着重说明“中文问题”,但本章所推出的结论却是适合于世界所有语言文字的。 概述 我们在实际开发中碰到的中文问题,真是形形色色,...
  • Velocity 用户指南手册中文版(转) 1. 关于 Velocity 用户指南旨在帮助页面设计者和内容提供者了解Velocity 和其简单而又强大的脚本语言(Velocity Template Language (VTL))。本指南中有很多示例展示了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,407
精华内容 562
关键字:

turbine的中文