少年阿賓

          那些青春的歲月

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

          #

          //研究了一下午,終于發現一個問題,寫這個代碼不是很難的,難的是找一個能代理的IP地址,實際網上的好多代碼都可以用滴,只是代理IP和Port有問題而已,廢話少說,直接上代碼:
          1、先說Get的代理(首先提供一個Servlet的Get的http服務)

          package com.abin.lee.servlet;

          import javax.servlet.ServletConfig;
          import javax.servlet.ServletException;
          import javax.servlet.ServletOutputStream;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.BufferedWriter;
          import java.io.IOException;
          import java.io.OutputStreamWriter;

          /**
           * Created with IntelliJ IDEA.
           * User: abin
           * Date: 13-4-18
           * Time: 上午8:39
           * To change this template use File | Settings | File Templates.
           */
          public class HttpClientGetProxyServlet extends HttpServlet {
              public void init(ServletConfig config) throws ServletException {
                  super.init(config);
              }
              public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException {
                  System.out.println("receive httpGet request");
                  String userName=request.getParameter("userName");
                  String passWord=request.getParameter("passWord");
                  String localIp=request.getLocalAddr();
                  String localName=request.getLocalName();
                  int localPort=request.getLocalPort();
                  int ServerPort=request.getServerPort();
                  String leeHeader=request.getHeader("lee");
                  System.out.println("userName="+userName+",passWord="+passWord+",localIp="+localIp+",localName="+localName+",localPort="+localPort);
                  System.out.println("ServerPort="+ServerPort+",leeHeader="+leeHeader);
                  String remoteIp=request.getRemoteAddr();
                  String remoteHost=request.getRemoteHost();
                  int remotePort=request.getRemotePort();
                  System.out.println("remoteIp="+remoteIp+",remoteHost="+remoteHost+",remotePort="+remotePort);

                  ServletOutputStream out=response.getOutputStream();
                  BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(out));
                  writer.write("success");
                  writer.flush();
                  writer.close();

              }
          }




          HttpGet代理測試類:

          package com.abin.lee.ssh.senior.proxy.httpclient;
          import org.apache.http.Header;
          import org.apache.http.HttpEntity;
          import org.apache.http.HttpHost;
          import org.apache.http.HttpResponse;
          import org.apache.http.client.methods.HttpGet;
          import org.apache.http.conn.params.ConnRoutePNames;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.apache.http.util.EntityUtils;
          import org.junit.Test;

          public class HttpClientGetProxyServletTest {
           public static final String HttpGetProxyUrl="http://localhost:8100/MyThread/HttpClientGetProxyServlet";
           @Test
              public  void testHttpClientPostProxyServlet()throws Exception {
                  HttpHost proxy = new HttpHost("10.10.10.10", 1443, "http");
                  DefaultHttpClient httpclient = new DefaultHttpClient();
                  try {
                      httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

                      HttpHost target = new HttpHost("localhost", 8100, "http");
                      HttpGet request = new HttpGet(HttpGetProxyUrl+"?"+"userName=abin&passWord=varyall");
                      request.setHeader("lee", "lee");
                      System.out.println("executing request to " + target + " via " + proxy);
                      HttpResponse rsp = httpclient.execute(target, request);
                      HttpEntity entity = rsp.getEntity();

                      System.out.println("----------------------------------------");
                      System.out.println(rsp.getStatusLine());
                      Header[] headers = rsp.getAllHeaders();
                      for (int i = 0; i<headers.length; i++) {
                          System.out.println(headers[i]);
                      }
                      System.out.println("----------------------------------------");

                      if (entity != null) {
                          System.out.println(EntityUtils.toString(entity));
                      }

                  } finally {
                      // When HttpClient instance is no longer needed,
                      // shut down the connection manager to ensure
                      // immediate deallocation of all system resources
                      httpclient.getConnectionManager().shutdown();
                  }
              }
          }







          //正常測試代碼,非代理

          package com.abin.lee.ssh.senior.proxy.httpclient;

          import java.io.BufferedReader;
          import java.io.InputStreamReader;

          import org.apache.http.HttpEntity;
          import org.apache.http.HttpResponse;
          import org.apache.http.client.HttpClient;
          import org.apache.http.client.methods.HttpGet;
          import org.apache.http.entity.StringEntity;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.junit.Test;

          public class HttpClientGetServletTest {
           public static final String HttpGetUrl = "http://localhost:8100/MyThread/HttpClientGetProxyServlet";

           @Test
           public void HttpClientGetServlet() {
            HttpClient httpClient = new DefaultHttpClient();
            StringEntity reqEntity = null;
            HttpGet httpGet = null;
            try {
             HttpGet request = new HttpGet(HttpGetUrl+"?"+"userName=abin&passWord=varyall");
                      request.setHeader("lee", "lee");
             // 目標地址
             System.out.println("請求: " + httpGet.getRequestLine());
             // 執行
             HttpResponse response = httpClient.execute(httpGet);
             HttpEntity entity = response.getEntity();
             System.out.println("----------------------------------------");
             System.out.println(response.getStatusLine());
             if (entity != null) {
              System.out.println("Response content length: "
                + entity.getContentLength());
             }
             // 顯示結果
             BufferedReader reader = new BufferedReader(new InputStreamReader(
               entity.getContent(), "UTF-8"));
             String line = null;
             while ((line = reader.readLine()) != null) {
              System.out.println(line);
             }
            } catch (Exception e) {
             e.printStackTrace();
            } finally {
             if (!httpGet.isAborted()) {
              httpGet.abort();
             }
             httpClient.getConnectionManager().shutdown();
            }
           }
          }




          //servlet配置

             <servlet>
                       <servlet-name>HttpClientGetProxyServlet</servlet-name>
                       <servlet-class>com.abin.lee.servlet.HttpClientGetProxyServlet</servlet-class>
                   </servlet>
                   <servlet-mapping>
                       <servlet-name>HttpClientGetProxyServlet</servlet-name>
                       <url-pattern>/HttpClientGetProxyServlet</url-pattern>
                   </servlet-mapping>






          我這里的IP和端口不一定能用喲,自己找能用的!!代碼是100%沒問題的,經過生產環境測試的喲!!!
          posted @ 2013-04-18 17:50 abin 閱讀(1436) | 評論 (0)編輯 收藏

          //研究了一下午,終于發現一個問題,寫這個代碼不是很難的,難的是找一個能代理的IP地址,實際網上的好多代碼都可以用滴,只是代理IP和Port有問題而已,廢話少說,直接上代碼:
          1、先說Post的代理
          //HttpClientPostProxyServlet

          package com.abin.lee.servlet;

          import javax.servlet.ServletConfig;
          import javax.servlet.ServletException;
          import javax.servlet.ServletOutputStream;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.BufferedWriter;
          import java.io.IOException;
          import java.io.OutputStreamWriter;

          /**
           * Created with IntelliJ IDEA.
           * User: abin
           * Date: 13-4-18
           * Time: 上午8:39
           * To change this template use File | Settings | File Templates.
           */
          public class HttpClientPostProxyServlet extends HttpServlet {
              public void init(ServletConfig config) throws ServletException {
                  super.init(config);
              }
              public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException {
                  System.out.println("receive httpPost request");
                  String userName=request.getParameter("userName");
                  String passWord=request.getParameter("passWord");
                  String localIp=request.getLocalAddr();
                  String localName=request.getLocalName();
                  int localPort=request.getLocalPort();
                  int serverPort=request.getServerPort();
                  String leeHeader=request.getHeader("lee");
                  System.out.println("userName="+userName+",passWord="+passWord+",localIp="+localIp+",localName="+localName+",localPort="+localPort);
                  System.out.println("serverPort="+serverPort+",leeHeader="+leeHeader);
                  String remoteIp=request.getRemoteAddr();
                  String remoteHost=request.getRemoteHost();
                  int remotePort=request.getRemotePort();
                  String remoteUser=request.getRemoteUser();
                  System.out.println("remoteIp="+remoteIp+",remoteHost="+remoteHost+",remotePort="+remotePort+",remoteUser="+remoteUser);

                  ServletOutputStream out=response.getOutputStream();
                  BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(out));
                  writer.write("success");
                  writer.flush();
                  writer.close();

              }
          }




          Post代理測試類:
          //HttpClientPostProxyServletTest.java

          package com.abin.lee.ssh.senior.proxy.httpclient;
          import org.apache.http.Header;
          import org.apache.http.HttpEntity;
          import org.apache.http.HttpHost;
          import org.apache.http.HttpResponse;
          import org.apache.http.client.methods.HttpPost;
          import org.apache.http.conn.params.ConnRoutePNames;
          import org.apache.http.entity.StringEntity;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.apache.http.util.EntityUtils;
          import org.junit.Test;

          public class HttpClientPostProxyServletTest {
           public static final String HttpPostProxyUrl="http://localhost:8100/MyThread/HttpClientPostProxyServlet";
           @Test
              public  void testHttpClientPostProxyServlet()throws Exception {
                  HttpHost proxy = new HttpHost("10.10.10.10", 1443, "http");

                  DefaultHttpClient httpclient = new DefaultHttpClient();
                  try {
                      httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

                      HttpHost target = new HttpHost("localhost", 8100, "http");
                      HttpPost request = new HttpPost(HttpPostProxyUrl);
                StringEntity reqEntity = new StringEntity("userName=abin&passWord=varyall");  
                reqEntity.setContentType("application/x-www-form-urlencoded");  
                request.setEntity(reqEntity);  
                request.setHeader("lee", "lee");
                      System.out.println("executing request to " + target + " via " + proxy);
                      HttpResponse rsp = httpclient.execute(target, request);
                      HttpEntity entity = rsp.getEntity();

                      System.out.println("----------------------------------------");
                      System.out.println(rsp.getStatusLine());
                      Header[] headers = rsp.getAllHeaders();
                      for (int i = 0; i<headers.length; i++) {
                          System.out.println(headers[i]);
                      }
                      System.out.println("----------------------------------------");

                      if (entity != null) {
                          System.out.println(EntityUtils.toString(entity));
                      }

                  } finally {
                      // When HttpClient instance is no longer needed,
                      // shut down the connection manager to ensure
                      // immediate deallocation of all system resources
                      httpclient.getConnectionManager().shutdown();
                  }
              }
          }




          //正常測試代碼,非代理

          package com.abin.lee.ssh.senior.proxy.httpclient;

          import java.io.BufferedReader;
          import java.io.InputStreamReader;

          import org.apache.http.HttpEntity;
          import org.apache.http.HttpResponse;
          import org.apache.http.client.HttpClient;
          import org.apache.http.client.methods.HttpPost;
          import org.apache.http.entity.StringEntity;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.junit.Test;

          public class HttpClientPostServletTest {
           public static final String HttpPostUrl="http://localhost:8100/MyThread/HttpClientPostProxyServlet";
           @Test
           public void testHttpClientPostServlet(){
             HttpClient httpClient = new DefaultHttpClient();
             HttpPost httpPost = new HttpPost(HttpPostUrl);
            try {
                // 目標地址  
                 System.out.println("請求: " + httpPost.getRequestLine());  
                // 構造最簡單的字符串數據  
                 StringEntity reqEntity = new StringEntity("userName=abin&passWord=varyall");  
                // 設置類型  
                 reqEntity.setContentType("application/x-www-form-urlencoded");  
                // 設置請求的數據  
                 httpPost.setEntity(reqEntity);  
                 httpPost.setHeader("lee", "lee");
                // 執行  
                 HttpResponse response = httpClient.execute(httpPost);  
                 HttpEntity entity = response.getEntity();  
                 System.out.println("----------------------------------------");  
                 System.out.println(response.getStatusLine());  
                if (entity != null) {  
                   System.out.println("Response content length: " + entity.getContentLength());  
                 }  
                // 顯示結果  
                 BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));  
                 String line = null;  
                while ((line = reader.readLine()) != null) {  
                   System.out.println(line);  
                 }  
            } catch (Exception e) {
             e.printStackTrace();
            }finally{
             if(!httpPost.isAborted()){
              httpPost.abort();
             }
             httpClient.getConnectionManager().shutdown();
            }
           }
          }



          //servlet配置
            <servlet>
                       <servlet-name>HttpClientPostProxyServlet</servlet-name>
                       <servlet-class>com.abin.lee.servlet.HttpClientPostProxyServlet</servlet-class>
                   </servlet>
                   <servlet-mapping>
                       <servlet-name>HttpClientPostProxyServlet</servlet-name>
                       <url-pattern>/HttpClientPostProxyServlet</url-pattern>
                   </servlet-mapping>




          我這里的IP和端口不一定能用喲,自己找能用的!!代碼是100%沒問題的,經過生產環境測試的喲!!!

          posted @ 2013-04-18 17:45 abin 閱讀(1171) | 評論 (1)編輯 收藏

               摘要: Introduction EasyMock (http://www.easymock.org/)使用Java的proxy機制即時為接口提供Mock Object (和擴展類的object)。因EasyMock獨特的期望記錄方式,大部分重構不會印象Mock Object,故EasyMock很適合于TDD。 EasyMock的license為Apache 2.0 (details see...  閱讀全文
          posted @ 2013-04-10 22:06 abin 閱讀(354) | 評論 (0)編輯 收藏

          使用simple-spring-memcached統一緩存的使用

          如何在一個中型的Java應用中使用Memcached緩存數據不是個簡單的問題。當某個緩存數據需要在多個系統間共享和失效時,必須要有統一的規劃才能保證不出錯。經過各種實踐,目前系統在使用Memcached緩存數據全部采用Simple-Spring-Memcached框架來完成,并統一規劃各系統Spring和Cache key的配置。
          下面對在使用過程中需要注意的點做一個詳細說明:

          Cache整體規劃

          目前我們系統中有兩個不同的Memcached服務器:

          1. session memcached服務器:主要存儲用戶的session
          2. app memcached服務器: 主要用于緩存應用數據

          由于應用所有的緩存數據都放在app緩存上,為避免各應用的緩存數據出現沖突,必須規劃好它們的命名空間。所幸Simple-Spring-Memcached支持namespace的概念,因此對各應用的namespace前綴規定如下:

          應用NAMESPACE前綴
          goodscentergoodscenter
          tradetrade
          uicuic

          這個namespace在生成key時,將放在最前面,稍后會有例子詳述。
          同一個應用中存在許多需要緩存的對象,因此約定namespace前綴之后再加上緩存對象的類名。
          例子如下:

          應用緩存對象完整的NAMESPACE最終生成的KEY
          tradeTcRate (id為42)trade:TcRatetrade:TcRate:12
          goodscenterGoodsDo(id為42)goodscenter:GoodsDogoodscenter:GoodsDo:12

          key的生成規則

          Simple-Spring-Memcached提供的針對單個對象的注解接口提供了兩種key生成方式,詳情見此文

          1. AssignCache類注解通過assignKey指定cache的key
          2. SingleCache類注解通過ParameterValueKeyProvider注解指定生成key的方法

          對于第一種只要求必須保證key不與其它的沖突,且namesapce符合規則。
          第二種時,約定緩存的數據對象必須實現有帶CacheKeyMethod的cacheKey方法,參考實現如下:

              @CacheKeyMethod     public String cacheKey() {         return this.getId();     }
          目前@CacheKeyMethod只支持返回String的方法,需要改造成可接受Long,Integer型的。當前必須有單獨的方法來作為緩存Key的生成器
          真實存放到Memcached的key的生成規則是:namespace:key。
          如goodscenter的id為42的domain對象GoodsDo,按上述方式生成的key為:goodscenter:GoodsDo:42

          spring配置說明

          關于Simple-Spring-Memcached具體XML配置如下:

          <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"        xmlns:context="http://www.springframework.org/schema/context"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">      <import resource="classpath:simplesm-context.xml"/>      <aop:aspectj -autoproxy/>     <context:annotation -config/>      <bean name="appCache" class="com.google.code.ssm.CacheFactory">         <property name="cacheClientFactory">             <bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/>         </property>         <property name="addressProvider">             <bean class="com.google.code.ssm.config.DefaultAddressProvider">                 <!--memcached服務器ip:port 可以是多個,格式為: 127.0.0.1:11211,192.168.100.11:11211-->                 <property name="address" value="{memcached.server}"/>             </bean>         </property>         <property name="configuration">             <!-- memcached連接器的配置,具體的配置項參考這個類 -->             <bean class="com.google.code.ssm.providers.XMemcachedConfiguration">                 <!--是否使用一致性哈希-->                 <property name="consistentHashing" value="true"/>                 <!--連接池-->                 <property name="connectionPoolSize" value="10"/>                 <property name="optimizeGet" value="true"/>              </bean>         </property>         <property name="cacheName">             <!-- 該Memcached配置的Cache名稱 一個應用中存在多個Memcached時,各個配置的cacheName必須不同。如果該值未設,系統默認為default -->             <value>appCache</value>         </property>     </bean> </beans>

          Java代碼中使用說明

          a. 注解方式使用

          直接使用注解來處理緩存及失效非常簡單,下面是相應的例子:
          讀取緩存:

          EventGoodsServiceClientImpl.java
              @Override     @ReadThroughSingleCache(namespace = "goodscenter:EventGoodsDo", expiration = 60)     @CacheName("appCache")     public EventGoodsDo queryEventGoodsDo(@ParameterValueKeyProvider(order = 0) long goodsId, @ParameterValueKeyProvider(order = 1) long eventId) {         return getRemoteServiceBean().queryEventGoodsDo(goodsId, eventId);     }

          更新緩存:

          EventGoodsDaoImpl.java
          @BridgeMethodMappings(value = {@BridgeMethodMapping(erasedParamTypes ={Object.class},targetParamTypes = {com.hqb360.promotion.dao.entity.EventGoods.class},methodName = "update")}) public class EventGoodsDaoImpl&lt;EventGoods&gt; extends BaseDaoImpl&lt;EventGoods&gt; implements EventGoodsDao&lt;EventGoods&gt; {      @Override     public DaoStatementName getDaoStatementName() {         return new DefaultDaoStatementName() {             public String getDomainName() {                 return "EventGoods";             }         };     }      @Override     @InvalidateSingleCache(namespace = "goodscenter:EventGoodsDo")     @CacheName("appCache")     public void update(@ParameterValueKeyProvider EventGoods obj) throws DataAccessException {         super.update(obj);     } }
          EventGoods.java
              @CacheKeyMethod     public String getCacheKey() {         return goodsId + CACHE_ID_SEPARATOR + eventId;     }      public static final String CACHE_ID_SEPARATOR = "/";
          上述代碼需要注意的點
          1. 多個方法參數都作為cacheKey時,ParameterValueKeyProvider必須指明其order值
          2. 多個方法參數作為cacheKey時,參數之間在 / 號分隔
          3. EventGoodsDaoImpl類中的update方法參數接收的是一個泛型對象,因此必須在該類上配置BridgeMethodMappings。具體配置見示例

          b. 以bean的方式使用Cache對象

          某些場景我們希望更便捷地自己手動來管理緩存數據,此時需要使用Simple-Spring-Memcached配置中定義的bean。以上面的配置文件為例,使用方法如下
          bean的注入:

          @Autowired private Cache appCache;

          bean的使用:

          appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);

          Posted in Java

           

          posted @ 2013-04-03 18:45 abin 閱讀(3739) | 評論 (0)編輯 收藏

          @CacheName指定緩存實例注解

          @CacheKeyMethod:緩存key生成注解

          ---------------------------------讀取-------------------------------------------

          @ReadThroughAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000): 讀取指定key緩存

          @ReadThroughSingleCache(namespace = SINGLE_NS, expiration = 0):讀取單個緩存

          @ReadThroughMultiCache(option = @ReadThroughMultiCacheOption(generateKeysFromResult = true)):讀取多個緩存

          @ReadThroughMultiCacheOption(generateKeysFromResult = true) 讀取多個緩存操作generateKeysFromResult 通過結果生成key

           

          ---------------------------------更新-------------------------------------------

          @UpdateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000): 指定key更新緩存

          @UpdateSingleCache(namespace = SINGLE_NS, expiration = 2): 更新單個緩存(namespace 命名空間, expiration 失效時間單位秒)

          @UpdateMultiCache(namespace = "Bravo", expiration = 300): 更新多個緩存

           

          ---------------------------------失效-------------------------------------------

          @InvalidateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo") : 指定key失效緩存

          @InvalidateSingleCache(namespace = SINGLE_NS):失效單個緩存

          @InvalidateMultiCache(namespace = "Delta") : 失效多個緩存

           

          ---------------------------------參數-------------------------------------------

          @ParameterDataUpdateContent標記方法的參數作為更新內容。這個注解應結合Update*Cache注解使用

          @ParameterValueKeyProvider: 標記將方法的參數做為計算緩存key.如果方法被注解的對象標記CacheKeyMethod的方法將會用來生成緩存key否則調用toString()生成

          @ParameterValueKeyProvider(order=0) 屬性表示如果多個參數做為key時需提供參數順序

          與@ParameterValueKeyProvider類似的注解有:

          {

            @ReturnValueKeyProvider返回值對象中計算key

          }

          ---------------------------------泛型處理-------------------------------------------

          @BridgeMethodMappings({ @BridgeMethodMapping(methodName = "updateUser", 

          erasedParamTypes = { Object.class }, targetParamTypes = { AppUser.class }) }): 泛型橋接注解

          methodName 指定方法

          erasedParamTypes 擦除對象類型

          targetParamTypes 目標轉換類型

           

           

          ---------------------------------計數器-------------------------------------------

          @InvalidateAssignCache  :在給的計算器上加1. 如果不存在則初始化為1

          @DecrementCounterInCache 在給的計數器上減1

           

          @ReadCounterFromCache  :讀取計數器

          @UpdateCounterFromCache 更新計數器

           

           

           

          Simple-Spring-Memcached代碼閱讀之BridgeMethod

           

          http://www.colorfuldays.org/program/java/bridgemethod%E7%9A%84%E4%BD%9C%E7%94%A8/

           

          http://www.colorfuldays.org/tag/ssm/   這個系列不錯

           

          b. 以bean的方式使用Cache對象

          某些場景我們希望更便捷地自己手動來管理緩存數據,此時需要使用Simple-Spring-Memcached配置中定義的bean。以上面的配置文件為例,使用方法如下
          bean的注入:

          @Autowired private Cache appCache;

          bean的使用:

          appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);
          posted @ 2013-04-03 18:43 abin 閱讀(1498) | 評論 (2)編輯 收藏

          主要是思維方式的不同: 

          顯然,RPC是以方法調用的方式描述WebSerivce的,也就是說,你要說清楚調用的那個方法,以及各個參數的名稱和值。要描述這些東東,SOAP消息就要有一個統一的規范,指出那一部分是方法名,哪個部分是參數,哪個部分是返回值。換句話說,RPC方式調用的SOAP消息格式是有章可循的,固定的。(比如說,每個Parameter必須對應一個Part,Part的name必須和參數名一致)。 

          而Document則是以文檔傳輸的方式描述WebService,只要你的SoapBody里面是一個可以用Schema描述的合法的Xml文檔就行了,對具體的格式沒有什么要求(Schema要在WSDL里面寫)。 

          可以看出,Document形式要更加靈活——尤其是需要傳輸特定格式的Xml文檔的時候,而RPC的Soap消息實際上也可以用Document形式模擬(只要Schema定義得當)。所以目前Document方式應用更廣泛一些(也是.NET里面的缺省方式)。 

          對Namespace,我覺得兩者應該沒有明顯的區別。主要是RPC通常與Encoding模式結合使用,這就要引用Soap的namespace了;而Document只要引用XmlSchema的Namespace定義類型就成了。

          posted @ 2013-04-02 15:26 abin 閱讀(559) | 評論 (0)編輯 收藏

          盡管是轉載的,但是原文中幾處錯誤或者不規范的表達,我已經進行了修改。
           
          大部分 Web 服務都是圍繞著遠程過程調用而構建的,而 WSDL 規范允許另外一種 Web 服務體系結構:文檔樣式( document style )。在該體系結構中,整個文檔在服務客戶端和服務器之間進行交換。在本文中, James McCarthy 將向您解釋文檔樣式以及應該何時使用它。
          Web 服務描述語言( Web Service Definition Language WDSL )規范中隱含著一個非常巧妙的轉換開關,它可以將 Web 服務的 SOAP 綁定從遠程過程調用轉換成 pass-through 文檔。在 SOAP 協議綁定中的樣式屬性可以包含這兩個值中的一個: rpc document 。當屬性被設定為文檔樣式時,客戶端知道應該使用 XML 模式而不是遠程過程調用約定。本文將提供對這個 WSDL 轉換開關的說明,描述它的好處,并將解釋應該何時使用 pass-through 文檔。
          首先,讓我們簡要地談談 WSDL 的一些要點,來理解這個巧妙的轉換是如何發生的。 WSDL 是一項 XML 規范,它被用來描述Web服務以及對于到達端點(服務)的協議相關的需求。 WSDL 用抽象術語來描述服務;通過可擴展的綁定定義,它能夠為使用具體術語調用服務定義協議和數據格式規范。下面的語法是直接從 WSDL 規范中摘 錄出來的,展示了在綁定中所包含的可擴展性元素:

          <wsdl:definitions .... >
              <wsdl:binding name="nmtoken" type="qname"> *
                  <-- extensibility element (1) --> *
                  <wsdl:operation name="nmtoken"> *
                     <-- extensibility element (2) --> *
                     <wsdl:input name="nmtoken"? > ?
                         <-- extensibility element (3) -->
                     </wsdl:input>
                     <wsdl:output name="nmtoken"? > ?
                         <-- extensibility element (4) --> *
                     </wsdl:output>
                     <wsdl:fault name="nmtoken"> *
                         <-- extensibility element (5) --> *
                     </wsdl:fault>
                  </wsdl:operation>
              </wsdl:binding>
          </wsdl:definitions>

           
          WSDL 規范通常描述三種綁定擴展: HTTP GET/POST MIME 以及 SOAP version 1.1 HTTP GET/POST MIME 中定義的綁定擴展用來定義與標準的 Web 應用程序進行通信的需
          求,這些應用程序可能返回(也可能不返回) XML 文檔。在發送或返回 XML 文檔時, HTTP GET/POST 綁定的擴展是隱式的文檔樣式。
          SOAP 綁定擴展用來定義支持 SOAP 信封協議的服務。 SOAP 信封是一種簡單模式,它設計成能包含 XML 消息,提供特定于應用程序的消息頭和消息體。 SOAP 綁定的擴展使 WSDL 文檔能夠聲明 SOAP 消息的需求,這樣應用程序就能夠與服務正確通信。 SOAP 擴展允許將 SOAP 消息的樣式聲明為文檔或 RPC 。如果在 soap:binding 元素中聲明了樣式屬性,那么該樣式將成為所有沒有顯式聲明的樣式屬性的 soap:operation 元素的缺省值。如果在 soap:binding 元素中沒有聲明樣式屬性,那么缺省的樣式就是文檔。下面是文檔樣式的顯式聲明:

          <soap:binding style="document" transport="uri">

           
          不管 soap:binding 元素中的聲明如何, soap:operation 元素可以覆蓋每個操作的聲明,就像這樣的:

           
          <soap:operation soapAction="uri" style="document">

           
          在聲明了文檔樣式的 SOAP 消息中,原始( as-is )或編碼( encoded )的消息被直接放置在 SOAP 信封的體部。
          如果樣式聲明為 RPC ,消息就封裝在包裝器元素中,同時帶有從操作名屬性中提取的的元素的名稱以及從操作名稱空間屬性中提取的名稱空間。
          勿庸置疑,使用 XML 調用跨平臺的遠程過程調用的能力是非常有用的,它是使用 Web 服務的非常有說服力的理由。但是如果 Web 服務僅僅局限于 RPC 消息傳遞,這項技術的影響將是有限的。幸運的是,開發人員可以選擇是使用 RPC 還是文檔樣式的消息傳遞,并且能夠使用適合的技術來完成他們面臨的任務。
          XML 規范開發用來使通常鎖定于專有格式的常規數據可以以一種人易讀的、自描述的、自驗證的開放格式來描述。當 Web 服務使用文檔消息傳遞時,它可以利用 XML 的全部能力來描述和驗證高級業務文檔。當服務使用 RPC 消息格式化時, XML 描述方法以及為方法調用編碼的參數,但是卻不能用來執行高級業務規則。為了執行這些規則, RPC 消息必須包含 XML 文檔作為字符串參數并且在被調用的方法中隱藏驗證。出于這個原因, XML 的某些好處就喪失了,或者至少是被隱藏在后臺應用程序里了。
          使用文檔消息傳遞的另外一個原因在于,遠程過程調用必須是相對靜態的,并且對接口的任何變化都將破壞服務和應用程序之間的契約。如果服務是廣泛分布的,那么很可能大量的應用程序已經從它的 WSDL 文檔中產生了存根代碼。改變 WSDL 將會導致所有依賴于特定方法簽名的應用程序被破壞,而且許多支持行產生問題。好的設計要求 RPC 消息服務的方法簽名不應該改變。使用文檔消息傳遞,規則更嚴格,并且可以使 XML 模式得到顯著增強和改變,同時又不會破壞調用應用程序。
          當業務使用基于 Web 的應用程序通過 Internet 交換信息時,應用程序應該能夠使用有保證的交付機制來提高它的可靠性、可伸縮性和性能。為了達到這個目的,應用程序通常將使用異步消息隊列。由于文檔消息通常是自包含的,所以它更適合于異步處理,并且可以直接放到隊列中。之所以說應用程序的可靠性得到了提高,是因為即使目標應用程序當前不是活動的,消息隊列也可以保證消息的交付;之所以說性能得到了提高,是因為 Web 應用程序只是把文檔發送到隊列中,然后便可以自由地執行其他的任務;之所以說可擴展性得到了提高,是因為文檔被下傳到一個或多個應用程序的實例以進行處理。
          業務文檔的設計通常很好地適于面向對象的體系結構。結果,兩個應用程序可以設計成通過使用 XML 交換對象的狀態。與對象序列化相比,在對象交換中,交換的每個端點都可以自由地設計它認為合適的對象,只要交換符合達成協議的 XML 文件格式即可。不使用對象序列化的一個原因是為了支持對象在客戶端和服務器端的實現。許多現有的特定于行業的 XML 模式被設計成客戶端 / 服務器體系結構,在這種體系結構中,在客戶端上完成的處理與預定在服務器上完成的處理是分離的。通常的情況是,客戶端僅僅以服務器要求的特定文檔格式請求或保存信息。當然,這種類型的交換也能用 RPC 消息完成,但是這種消息的編碼方式限制了每個端點上的對象的設計。在文檔樣式中就不會出現這些限制問題。
          什么時候應該使用文檔樣式呢?簡單地說:只要沒有連接到已存在的遠程過程調用,任何時候都可以使用文檔方式。使用文檔方式比起通常花費額外的工作來連接服務,好處要大得多。不過需要提醒的是:一般來說,構建一個使用文檔消息傳遞的服務的工作量要比構建一個 RPC 消息服務所需的工作量大。這些額外的工作通常包括 XML 模式的設計或對已存在的模式的支持、以及從文檔中提取相關的信息。模式設計是重要的,因為 XML 解析器使用這個模式來驗證文檔,支持預定的業務規則。服務需要進行額外的工作來從文檔中提取用于處理請求的相關信息。相比之下, RPC 消息只需要設計方法的接口,通過方法的接口, RPC 消息就可以自動地編組和解組參數。
          當您決定發布一項服務時,您可能應該考慮下列問題。我將在下面的部分中分析您的答案的結果。
          • 這項服務是連接到已存在的過程調用,并且這個過程調用是無狀態的嗎?
          • 這項服務是僅在您的組織內部使用,還是也可以被外部用戶所使用?
          • 參數之一僅僅是 XML 文檔規范嗎?
          • 這項服務需要請求 / 響應體系結構嗎?
          • 參數表示了可以從用于驗證的 XML 文檔模式受益的復雜結構嗎?
          • 所有需要進行交換的信息都能夠合理地存放在內存中嗎?
           如果必須以特定的順序調用多個過程來維護應用程序狀態,您應該考慮在您的服務中使用文檔體系結構。如果需要多個過程調用,那么過程就不是無狀態的,并且服務必須維護應用程序狀態。在 Web 服務中維護狀態可能是困難的;在遠程過程調用的情況下,很少有客戶端平臺會產生能夠支持狀態信息的存根代碼。一個可能的解決方案是使用文檔體系結構,并在文檔內部傳送整個事務的內容。在這種情況下,服務將執行調用,以確保服務內部保持正確的順序,并且狀態信息的維護不超出單個事務的范圍。如果仍然需要狀態信息,可以將狀態信息記錄在最終得到的文檔中,客戶端應用程序也可以維護一個用于服務識別它的狀態的令牌。
          如果一個應用程序被發布到組織以外,發布者就很難控制誰正依賴于這個服務,以及如果做出任何改動后果會怎樣。在這種情況下,使用文檔消息傳遞和支持像 ebXML 這樣的通用交換協議可能更加有利。通用交換協議正發展成能改善外部交換的管理,因此新的貿易合作伙伴協定就可以快速地部署。同樣,如果您的服務不需要請求 / 響應體系結構,那么通用交換協議就可以更好地設計來處理認證、可靠消息交付以及異步請求 / 響應。
          如果您的服務正使用字符串參數來傳遞或返回 XML 文檔,或者它的參數之一是一個具有復雜結構且需要自定義處理的對象,那么文檔消息傳遞就可能是較好的選擇。將參數的真實含義隱藏在字符串里經常會導致帶有無效參數的有效調用。如果服務發布了 XML 文檔模式,那么在調用服務之前根據這個模式進行驗證就會更加容易。復雜結構經常用來傳遞組成完整事務的數百條信息。在處理復雜的結構時,遠程過程服務可能不得不處理自定義的編組代碼,同時應用程序仍然負責仔細地驗證結構的每個元素。如果使用文檔消息傳遞,那么應用程序程序員就可以使用 XML 模式來將驗證下傳到文檔設計器,并且不需要自定義的編組代碼。
              擇使用文檔樣式的消息傳遞還是 RPC 樣式的消息傳遞時,需要考慮的最后一個因素是需要處理的信息量大小。由于采用 RPC 樣式的消息傳遞來編組參數的大部分(如果不是全部的話)實現都是在內存中執行這項操作,所以內存約束可能會使得 RPC 消息傳遞行不通。許多文檔消息傳遞服務能夠選擇是用 DOM 還是用 SAX 來處理文檔,因而能夠最小化內存中的處理。這對于 Web 服務尤為關鍵,因為它可能需要處理成千上萬的請求,而且其中許多是同時發生的。
          在您設計下一個 Web 服務時,您需要考慮當前的 WSDL 規范為您提供的所有選擇。在開始創建過程性的接口之前,考慮好將如何使用服務,誰將使用它,以及需要交換的數據的類型和數量。設計開發文檔樣式的 Web 服務可能需要稍多一些的工作量,但是在很多情況下,這些額外的工作量將會換取更高的信息質量和更可靠的交換性能。
          posted @ 2013-04-02 15:02 abin 閱讀(1389) | 評論 (0)編輯 收藏

          TCP/IP:
          數據鏈路層:ARP,RARP
          網絡層: IP,ICMP,IGMP
          傳輸層:TCP ,UDP,UGP
          應用層:Telnet,FTP,SMTP,SNMP.

          OSI:
          物理層:EIA/TIA-232, EIA/TIA-499, V.35, V.24, RJ45, Ethernet, 802.3, 802.5, FDDI, NRZI, NRZ, B8ZS
          數據鏈路層:Frame Relay, HDLC, PPP, IEEE 802.3/802.2, FDDI, ATM,  IEEE 802.5/802.2
          網絡層:IP,IPX,AppleTalk DDP
          傳輸層:TCP,UDP,SPX
          會話層:RPC,SQL,NFS,NetBIOS,names,AppleTalk,ASP,DECnet,SCP
          表示層:TIFF,GIF,JPEG,PICT,ASCII,EBCDIC,encryption,MPEG,MIDI,HTML
          應用層:FTP,WWW,Telnet,NFS,SMTP,Gateway,SNMP

          應用層
          1.主要功能 :用戶接口、應用程序
          application 2.典型設備:網關
          3.典型協議、標準和應用:TELNET, FTP, HTTP

          表示層
          1.主要功能 :數據的表示、壓縮和加密
          presentation2.典型設備:網關
          3.典型協議、標準和應用:ASCLL、PICT、TIFF、JPEG、 MIDI、MPEG

          會話層
          1.主要功能 :會話的建立和結束
          session2.典型設備:網關
          3.典型協議、標準和應用:RPC、SQL、NFS 、X WINDOWS、ASP


          傳輸層
          1.主要功能 :端到端控制
          transport 2.典型設備:網關
          3.典型協議、標準和應用:TCP、UDP、SPX

          網絡層
          1.主要功能 :路由,尋址
          network2.典型設備:路由器
          3.典型協議、標準和應用:IP、IPX、APPLETALK、ICMP

          數據鏈路層
          1.主要功能 :保證誤差錯的數據鏈路
          data link 2.典型設備:交換機、網橋、網卡
          3.典型協議、標準和應用:802.2、802.3ATM、HDLC、FRAME RELAY

          物理層
          1.主要功能 :傳輸比特流
          physical2.典型設備:集線器、中繼器
          3.典型協議、標準和應用:V.35、EIA/TIA-232

          從下到上,物理層最低的!!!!應用層最高。

          什么是TCP/IP協議,劃為幾層,各有什么功能?
          TCP/IP協議族包含了很多功能各異的子協議。為此我們也利用上文所述的分層的方式來剖析它的結構。TCP/IP層次模型共分為四層:應用層、傳輸層、網絡層、數據鏈路層。

          TCP/IP網絡協議
          TCP/IP(Transmission Control Protocol/Internet Protocol,傳輸控制協議/網間網協議)是目前世界上應用最為廣泛的協議,它的流行與Internet的迅猛發展密切相關—TCP/IP最初是為互聯網的原型ARPANET所設計的,目的是提供一整套方便實用、能應用于多種網絡上的協議,事實證明TCP/IP做到了這一點,它使網絡互聯變得容易起來,并且使越來越多的網絡加入其中,成為Internet的事實標準。

          * 應用層—應用層是所有用戶所面向的應用程序的統稱。ICP/IP協議族在這一層面有著很多協議來支持不同的應用,許多大家所熟悉的基于Internet的應用的實現就離不開這些協議。如我們進行萬維網(WWW)訪問用到了HTTP協議、文件傳輸用FTP協議、電子郵件發送用SMTP、域名的解析用DNS協議、遠程登錄用Telnet協議等等,都是屬于TCP/IP應用層的;就用戶而言,看到的是由一個個軟件所構筑的大多為圖形化的操作界面,而實際后臺運行的便是上述協議。

          * 傳輸層—這一層的的功能主要是提供應用程序間的通信,TCP/IP協議族在這一層的協議有TCP和UDP。

          * 網絡層—是TCP/IP協議族中非常關鍵的一層,主要定義了IP地址格式,從而能夠使得不同應用類型的數據在Internet上通暢地傳輸,IP協議就是一個網絡層協議。

          * 網絡接口層—這是TCP/IP軟件的最低層,負責接收IP數據包并通過網絡發送之,或者從網絡上接收物理幀,抽出IP數據報,交給IP層。

          1.TCP/UDP協議
          TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)協議屬于傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸,它提供的服務包括數據流傳送、可靠性、有效流控、全雙工操作和多路復用。通過面向連接、端到端和可靠的數據包發送。通俗說,它是事先為所發送的數據開辟出連接好的通道,然后再進行數據發送;而UDP則不為IP提供可靠性、流控或差錯恢復功能。一般來說,TCP對應的是可靠性要求高的應用,而UDP對應的則是可靠性要求低、傳輸經濟的應用。TCP支持的應用協議主要有:Telnet、FTP、SMTP等;UDP支持的應用層協議主要有:NFS(網絡文件系統)、SNMP(簡單網絡管理協議)、DNS(主域名稱系統)、TFTP(通用文件傳輸協議)等。

          IP協議的定義、IP地址的分類及特點

          什么是IP協議,IP地址如何表示,分為幾類,各有什么特點?
          為了便于尋址和層次化地構造網絡,IP地址被分為A、B、C、D、E五類,商業應用中只用到A、B、C三類。

          IP協議(Internet Protocol)又稱互聯網協議,是支持網間互連的數據報協議,它與TCP協議(傳輸控制協議)一起構成了TCP/IP協議族的核心。它提供網間連接的完善功能, 包括IP數據報規定互連網絡范圍內的IP地址格式。

          Internet 上,為了實現連接到互聯網上的結點之間的通信,必須為每個結點(入網的計算機)分配一個地址,并且應當保證這個地址是全網唯一的,這便是IP地址。

          目前的IP地址(IPv4:IP第4版本)由32個二進制位表示,每8位二進制數為一個整數,中間由小數點間隔,如159.226.41.98,整個IP地址空間有4組8位二進制數,由表示主機所在的網絡的地址(類似部隊的編號)以及主機在該網絡中的標識(如同士兵在該部隊的編號)共同組成。

          為了便于尋址和層次化的構造網絡,IP地址被分為A、B、C、D、E五類,商業應用中只用到A、B、C三類。

          * A類地址:A類地址的網絡標識由第一組8位二進制數表示,網絡中的主機標識占3組8位二進制數,A類地址的特點是網絡標識的第一位二進制數取值必須為 “0”。不難算出,A類地址允許有126個網段,每個網絡大約允許有1670萬臺主機,通常分配給擁有大量主機的網絡(如主干網)。

          * B類地址:B類地址的網絡標識由前兩組8位二進制數表示,網絡中的主機標識占兩組8位二進制數,B類地址的特點是網絡標識的前兩位二進制數取值必須為“10”。B類地址允許有16384個網段,每個網絡允許有65533臺主機,適用于結點比較多的網絡(如區域網)。

          * C類地址:C類地址的網絡標識由前3組8位二進制數表示,網絡中主機標識占1組8位二進制數,C類地址的特點是網絡標識的前3位二進制數取值必須為“110”。具有C類地址的網絡允許有254臺主機,適用于結點比較少的網絡(如校園網)。

          為了便于記憶,通常習慣采用4個十進制數來表示一個IP地址,十進制數之間采用句點“.”予以分隔。這種IP地址的表示方法也被稱為點分十進制法。如以這種方式表示,A類網絡的IP地址范圍為1.0.0.1-127.255.255.254;B類網絡的IP地址范圍為:128.1.0.1-191.255.255.254;C類網絡的IP地址范圍為:192.0.1.1-223.255.255.254。

          由于網絡地址緊張、主機地址相對過剩,采取子網掩碼的方式來指定網段號。

          TCP/IP協議與低層的數據鏈路層和物理層無關,這也是TCP/IP的重要特點。正因為如此 ,它能廣泛地支持由低兩層協議構成的物理網絡結構。目前已使用TCP/IP連接成洲際網、全國網與跨地區網。

          OSP與TCP/IP的參考層次圖:

           

          OSI七層協議和TCP/IP四層協議之比較

           

          OSP與TCP/IP的比較:

          分層結構
          OSI參考模型與TCP/IP協議都采用了分層結構,都是基于獨立的協議棧的概念。OSI參考模型有7層,而TCP/IP協議只有4層,即TCP/IP協議沒有了表示層和會話層,并且把數據鏈路層和物理層合并為網絡接口層。不過,二者的分層之間有一定的對應關系

          標準的特色
          OSI參考模型的標準最早是由ISO和CCITT(ITU的前身)制定的,有濃厚的通信背景,因此也打上了深厚的通信系統的特色,比如對服務質量(QoS)、差錯率的保證,只考慮了面向連接的服務。并且是先定義一套功能完整的構架,再根據該構架來發展相應的協議與系統。

          TCP/IP協議產生于對Internet網絡的研究與實踐中,是應實際需求而產生的,再由IAB、IETF等組織標準化,而并不是之前定義一個嚴謹的框架。而且TCP/IP最早是在UNIX系統中實現的,考慮了計算機網絡的特點,比較適合計算機實現和使用。

          連接服務
          OSI的網絡層基本與TCP/IP的網際層對應,二者的功能基本相似,但是尋址方式有較大的區別。

          OSI的地址空間為不固定的可變長,由選定的地址命名方式決定,最長可達160byte,可以容納非常大的網絡,因而具有較大的成長空間。根據OSI的規定,網絡上每個系統至多可以有256個通信地址。

          TCP/IP網絡的地址空間為固定的4byte(在目前常用的IPV4中是這樣,在IPV6中將擴展到16byte)。網絡上的每一個系統至少有一個唯一的地址與之對應。

          傳輸服務
          OSI與TCP/IP的傳輸層都對不同的業務采取不同的傳輸策略。OSI定義了五個不同層次的服務:TP1,TP2,TP3,TP4,TP5。TCP/IP定義了TCP和UPD兩種協議,分別具有面向連接和面向無連接的性質。其中TCP與OSI中的TP4,UDP與OSI中的TP0在構架和功能上大體相同,只是內部細節有一些差異。

          應用范圍
          OSI由于體系比較復雜,而且設計先于實現,有許多設計過于理想,不太方便計算機軟件實現,因而完全實現OSI參考模型的系統并不多,應用的范圍有限。而TCP/IP協議最早在計算機系統中實現,在UNIX、Windows平臺中都有穩定的實現,并且提供了簡單方便的編程接口(API),可以在其上開發出豐富的應用程序,因此得到了廣泛的應用。TCP/IP協議已成為目前網際互聯事實上的國際標準和工業標準。

          posted @ 2013-03-28 21:26 abin 閱讀(833) | 評論 (0)編輯 收藏

           數據庫連接池在初始化的時候會創建initialSize個連接,當有數據庫操作時,會從池中取出一個連接。如果當前池中正在使用的連接數等于maxActive,則會等待一段時間,等待其他操作釋放掉某一個連接,如果這個等待時間超過了maxWait,則會報錯;如果當前正在使用的連接數沒有達到maxActive,則判斷當前是否空閑連接,如果有則直接使用空閑連接,如果沒有則新建立一個連接。在連接使用完畢后,不是將其物理連接關閉,而是將其放入池中等待其他操作復用。
            對于一個數據庫連接池,一般包括一下屬性。
            
            
            名稱   說明   備注
          minIdle 最小活躍數 可以允許的空閑連接數目
          maxActive 最大連接數 相當于池的大小
          initialSize 初始化連接大小
          maxWait 獲取連接的最大等待時間
          timeBetweenEvictionRunsMillis 空閑連接的最大生存時間 指定空閑連接在空閑多長時間后,關閉其物理連接
          testWhileIdle 是否在空閑時檢測連接可用性 由于網絡或者數據庫配置的原因(比如mysql連接的8小時限制),開啟這個開關可以定期檢測連接的可用性
          posted @ 2013-03-27 10:32 abin 閱讀(563) | 評論 (0)編輯 收藏


           

          鎖:

          1. 內置鎖 (監視器鎖): 每個java對象都可以做一個實現同步的鎖,這些鎖被成為內置鎖. 獲得鎖的唯一途徑就是進入有這個鎖保護的代碼塊或方法
          2. 重入鎖: 由于內置鎖是可重入的,因此如果某個線程試圖獲得一個以已經由他自己持有的鎖, 那么這個請求就會成功.重入意味著獲取鎖的操作粒度是"線程",而不是"調用"

          volatile 使用條件(必須同時滿足所有條件):

          1. 對變量的寫入操作不依賴變量的當前值,或者你能確保只有單個線程更新變量的值
          2. 該變量不會與其他狀態變量一起納入不變性條件中
          3. 在訪問變量時間不需要加鎖


           


           

          高并發術語



          術語

          英文單詞

          描述

          比較并交換

          Compare and Swap

          CAS操作需要輸入兩個數值,一個舊值(期望操作前的值)和一個新值,在操作期間先比較下舊值有沒有發生變化,如果沒有發生變化,才交換成新值,發生了變化則不交換

          CPU流水線

          CPU pipeline

          CPU流水線的工作方式就象工業生產上的裝配流水線,在CPU中由5~6個不同功能的電路單元組成一條指令處理流水線,然后將一條X86指令分成5~6步后再由這些電路單元分別執行,這樣就能實現在一個CPU時鐘周期完成一條指令,因此提高CPU的運算速度

          內存順序沖突

          Memory order violation

          內存順序沖突一般是由假共享引起,假共享是指多個CPU同時修改同一個緩存行的不同部分而引起其中一個CPU的操作無效,當出現這個內存順序沖突時,CPU必須清空流水線

          共享變量


          在多個線程之間能夠被共享的變量被稱為共享變量。共享變量包括所有的實例變量,靜態變量和數組元素。他們都被存放在堆內存中,Volatile只作用于共享變量。

          內存屏障

          Memory Barriers

          是一組處理器指令,用于實現對內存操作的順序限制。

          緩沖行

          Cache line

          緩存中可以分配的最小存儲單位。處理器填寫緩存線時會加載整個緩存線,需要使用多個主內存讀周期。

          原子操作

          Atomic operations

          不可中斷的一個或一系列操作。

          緩存行填充

          cache line fill

          當處理器識別到從內存中讀取操作數是可緩存的,處理器讀取整個緩存行到適當的緩存(L1,L2,L3的或所有)

          緩存命中

          cache hit

          如果進行高速緩存行填充操作的內存位置仍然是下次處理器訪問的地址時,處理器從緩存中讀取操作數,而不是從內存。

          寫命中

          write hit

          當處理器將操作數寫回到一個內存緩存的區域時,它首先會檢查這個緩存的內存地址是否在緩存行中,如果存在一個有效的緩存行,則處理器將這個操作數寫回到緩存,而不是寫回到內存,這個操作被稱為寫命中。



           

          synchronized

          volatile

          concurrent 

          在并發編程中很常用的實用工具類。此包包括了幾個小的、已標準化的可擴展框架,以及一些提供有用功能的類,沒有這些類,這些功能會很難實現或實現起來冗長乏味。下面簡要描述主要的組件。另請參閱 locks 和 atomic 包。

          執行程序

          接口。Executor 是一個簡單的標準化接口,用于定義類似于線程的自定義子系統,包括線程池、異步 IO 和輕量級任務框架。根據所使用的具體 Executor 類的不同,可能在新創建的線程中,現有的任務執行線程中,或者調用 execute() 的線程中執行任務,并且可能順序或并發執行。ExecutorService 提供了多個完整的異步任務執行框架。ExecutorService 管理任務的排隊和安排,并允許受控制的關閉。ScheduledExecutorService 子接口及相關的接口添加了對延遲的和定期任務執行的支持。ExecutorService 提供了安排異步執行的方法,可執行由 Callable 表示的任何函數,結果類似于 RunnableFuture 返回函數的結果,允許確定執行是否完成,并提供取消執行的方法。RunnableFuture 是擁有 run 方法的 Future,run 方法執行時將設置其結果。

          實現。類 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 提供可調的、靈活的線程池。Executors 類提供大多數 Executor 的常見類型和配置的工廠方法,以及使用它們的幾種實用工具方法。其他基于 Executor 的實用工具包括具體類 FutureTask,它提供 Future 的常見可擴展實現,以及 ExecutorCompletionService,它有助于協調對異步任務組的處理。

          隊列

          java.util.concurrent ConcurrentLinkedQueue 類提供了高效的、可伸縮的、線程安全的非阻塞 FIFO 隊列。java.util.concurrent 中的五個實現都支持擴展的 BlockingQueue 接口,該接口定義了 put 和 take 的阻塞版本:LinkedBlockingQueueArrayBlockingQueueSynchronousQueuePriorityBlockingQueue 和 DelayQueue。這些不同的類覆蓋了生產者-使用者、消息傳遞、并行任務執行和相關并發設計的大多數常見使用的上下文。BlockingDeque 接口擴展 BlockingQueue,以支持 FIFO 和 LIFO(基于堆棧)操作。LinkedBlockingDeque 類提供一個實現。

          計時

          TimeUnit 類為指定和控制基于超時的操作提供了多重粒度(包括納秒級)。該包中的大多數類除了包含不確定的等待之外,還包含基于超時的操作。在使用超時的所有情況中,超時指定了在表明已超時前該方法應該等待的最少時間。在超時發生后,實現會“盡力”檢測超時。但是,在檢測超時與超時之后再次實際執行線程之間可能要經過不確定的時間。接受超時期參數的所有方法將小于等于 0 的值視為根本不會等待。要“永遠”等待,可以使用 Long.MAX_VALUE 值。

          同步器

          四個類可協助實現常見的專用同步語句。Semaphore 是一個經典的并發工具。CountDownLatch 是一個極其簡單但又極其常用的實用工具,用于在保持給定數目的信號、事件或條件前阻塞執行。CyclicBarrier 是一個可重置的多路同步點,在某些并行編程風格中很有用。Exchanger 允許兩個線程在 collection 點交換對象,它在多流水線設計中是有用的。

          并發 Collection

          除隊列外,此包還提供了設計用于多線程上下文中的 Collection 實現:ConcurrentHashMapConcurrentSkipListMapConcurrentSkipListSetCopyOnWriteArrayList 和 CopyOnWriteArraySet。當期望許多線程訪問一個給定 collection 時,ConcurrentHashMap 通常優于同步的 HashMapConcurrentSkipListMap 通常優于同步的 TreeMap。當期望的讀數和遍歷遠遠大于列表的更新數時,CopyOnWriteArrayList 優于同步的 ArrayList

          此包中與某些類一起使用的“Concurrent&rdquo前綴;是一種簡寫,表明與類似的“同步”類有所不同。例如,java.util.Hashtable 和Collections.synchronizedMap(new HashMap()) 是同步的,但 ConcurrentHashMap 則是“并發的”。并發 collection 是線程安全的,但是不受單個排他鎖的管理。在 ConcurrentHashMap 這一特定情況下,它可以安全地允許進行任意數目的并發讀取,以及數目可調的并發寫入。需要通過單個鎖不允許對 collection 的所有訪問時,“同步”類是很有用的,其代價是較差的可伸縮性。在期望多個線程訪問公共 collection 的其他情況中,通常“并發”版本要更好一些。當 collection 是未共享的,或者僅保持其他鎖時 collection 是可訪問的情況下,非同步 collection 則要更好一些。

          大多數并發 Collection 實現(包括大多數 Queue)與常規的 java.util 約定也不同,因為它們的迭代器提供了弱一致的,而不是快速失敗的遍歷。弱一致的迭代器是線程安全的,但是在迭代時沒有必要凍結 collection,所以它不一定反映自迭代器創建以來的所有更新。

          內存一致性屬性

          Java Language Specification 第 17 章定義了內存操作(如共享變量的讀寫)的 happen-before 關系。只有寫入操作 happen-before 讀取操作時,才保證一個線程寫入的結果對另一個線程的讀取是可視的。synchronized 和 volatile 構造 happen-before 關系,Thread.start() 和Thread.join() 方法形成 happen-before 關系。尤其是:
          • 線程中的每個操作 happen-before 稍后按程序順序傳入的該線程中的每個操作。
          • 一個解除鎖監視器的(synchronized 阻塞或方法退出)happen-before 相同監視器的每個后續鎖(synchronized 阻塞或方法進入)。并且因為 happen-before 關系是可傳遞的,所以解除鎖定之前的線程的所有操作 happen-before 鎖定該監視器的任何線程后續的所有操作。
          • 寫入 volatile 字段 happen-before 每個后續讀取相同字段。volatile 字段的讀取和寫入與進入和退出監視器具有相似的內存一致性效果,但 需要互斥鎖。
          • 在線程上調用 start happen-before 已啟動的線程中的任何線程。
          • 線程中的所有操作 happen-before 從該線程上的 join 成功返回的任何其他線程。
          java.util.concurrent 中所有類的方法及其子包擴展了這些對更高級別同步的保證。尤其是:
          • 線程中將一個對象放入任何并發 collection 之前的操作 happen-before 從另一線程中的 collection 訪問或移除該元素的后續操作。
          • 線程中向 Executor 提交 Runnable 之前的操作 happen-before 其執行開始。同樣適用于向 ExecutorService 提交 Callables
          • 異步計算(由 Future 表示)所采取的操作 happen-before 通過另一線程中 Future.get() 獲取結果后續的操作。
          • “釋放”同步儲存方法(如 Lock.unlockSemaphore.release 和 CountDownLatch.countDown)之前的操作 happen-before 另一線程中相同同步儲存對象成功“獲取”方法(如 Lock.lockSemaphore.acquireCondition.await 和 CountDownLatch.await)的后續操作。
          • 對于通過 Exchanger 成功交換對象的每個線程對,每個線程中 exchange() 之前的操作 happen-before 另一線程中對應 exchange() 后續的操作。
          • 調用 CyclicBarrier.await 之前的操作 happen-before 屏障操作所執行的操作,屏障操作所執行的操作 happen-before 從另一線程中對應await 成功返回的后續操作。

           

          Condition

          Condition 將 Object 監視器方法(waitnotify 和 notifyAll)分解成截然不同的對象,以便通過將這些對象與任意 Lock 實現組合使用,為每個對象提供多個等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。

          條件(也稱為條件隊列 或條件變量)為線程提供了一個含義,以便在某個狀態條件現在可能為 true 的另一個線程通知它之前,一直掛起該線程(即讓其“等待”)。因為訪問此共享狀態信息發生在不同的線程中,所以它必須受保護,因此要將某種形式的鎖與該條件相關聯。等待提供一個條件的主要屬性是:以原子方式 釋放相關的鎖,并掛起當前線程,就像 Object.wait 做的那樣。

          Condition 實例實質上被綁定到一個鎖上。要為特定 Lock 實例獲得 Condition 實例,請使用其 newCondition() 方法。

          作為一個示例,假定有一個綁定的緩沖區,它支持 put 和 take 方法。如果試圖在空的緩沖區上執行 take 操作,則在某一個項變得可用之前,線程將一直阻塞;如果試圖在滿的緩沖區上執行 put 操作,則在有空間變得可用之前,線程將一直阻塞。我們喜歡在單獨的等待 set 中保存 put 線程和 take 線程,這樣就可以在緩沖區中的項或空間變得可用時利用最佳規劃,一次只通知一個線程。可以使用兩個 Condition 實例來做到這一點。

           class BoundedBuffer {
             final Lock lock = new ReentrantLock();
             final Condition notFull  = lock.newCondition(); 
             final Condition notEmpty = lock.newCondition(); 
          
             final Object[] items = new Object[100];
             int putptr, takeptr, count;
          
             public void put(Object x) throws InterruptedException {
               lock.lock();
               try {
                 while (count == items.length) 
                   notFull.await();
                 items[putptr] = x; 
                 if (++putptr == items.length) putptr = 0;
                 ++count;
                 notEmpty.signal();
               } finally {
                 lock.unlock();
               }
             }
          
             public Object take() throws InterruptedException {
               lock.lock();
               try {
                 while (count == 0) 
                   notEmpty.await();
                 Object x = items[takeptr]; 
                 if (++takeptr == items.length) takeptr = 0;
                 --count;
                 notFull.signal();
                 return x;
               } finally {
                 lock.unlock();
               }
             } 
           }
           
          
          
          ArrayBlockingQueue 類提供了這項功能,因此沒有理由去實現這個示例類。)

          Condition 實現可以提供不同于 Object 監視器方法的行為和語義,比如受保證的通知排序,或者在執行通知時不需要保持一個鎖。如果某個實現提供了這樣特殊的語義,則該實現必須記錄這些語義。

          注意,Condition 實例只是一些普通的對象,它們自身可以用作 synchronized 語句中的目標,并且可以調用自己的 wait 和notification 監視器方法。獲取 Condition 實例的監視器鎖或者使用其監視器方法,與獲取和該 Condition 相關的 Lock 或使用其 waiting 和 signalling 方法沒有什么特定的關系。為了避免混淆,建議除了在其自身的實現中之外,切勿以這種方式使用Condition 實例。

          除非另行說明,否則為任何參數傳遞 null 值將導致拋出 NullPointerException

          實現注意事項

          在等待 Condition 時,允許發生“虛假喚醒”,這通常作為對基礎平臺語義的讓步。對于大多數應用程序,這帶來的實際影響很小,因為 Condition 應該總是在一個循環中被等待,并測試正被等待的狀態聲明。某個實現可以隨意移除可能的虛假喚醒,但建議應用程序程序員總是假定這些虛假喚醒可能發生,因此總是在一個循環中等待。

          三種形式的條件等待(可中斷、不可中斷和超時)在一些平臺上的實現以及它們的性能特征可能會有所不同。尤其是它可能很難提供這些特性和維護特定語義,比如排序保證。更進一步地說,中斷線程實際掛起的能力在所有平臺上并不是總是可行的。

          因此,并不要求某個實現為所有三種形式的等待定義完全相同的保證或語義,也不要求其支持中斷線程的實際掛起。

          要求實現清楚地記錄每個等待方法提供的語義和保證,在某個實現不支持中斷線程的掛起時,它必須遵從此接口中定義的中斷語義。

          由于中斷通常意味著取消,而又通常很少進行中斷檢查,因此實現可以先于普通方法的返回來對中斷進行響應。即使出現在另一個操作后的中斷可能會釋放線程鎖時也是如此。實現應記錄此行為。





          http://blog.csdn.net/fh13760184/article/details/8551546#
          posted @ 2013-03-21 16:03 abin 閱讀(2365) | 評論 (3)編輯 收藏

          僅列出標題
          共50頁: First 上一頁 14 15 16 17 18 19 20 21 22 下一頁 Last 
          主站蜘蛛池模板: 平远县| 专栏| 武宣县| 全南县| 汪清县| 西峡县| 勃利县| 蒲江县| 普兰县| 乌苏市| 苏尼特左旗| 阳朔县| 德格县| 万州区| 仪陇县| 武胜县| 华容县| 宜黄县| 昌黎县| 时尚| 同德县| 青海省| 海安县| 九龙城区| 阳曲县| 中宁县| 鲁山县| 襄汾县| 赤水市| 本溪市| 吴忠市| 菏泽市| 南郑县| 万荣县| 东平县| 嘉义市| 同心县| 秦安县| 铁力市| 周宁县| 延寿县|