Winse Blog

走走停停都是风景, 熙熙攘攘都向最好, 忙忙碌碌都为明朝, 何畏之.

Java中使用代理-基于Shandowsocks

在开发过程中,时不时需要要代理一下,来访问我们需要的资源,比方说:DEBUG生产集群的应用、还有在Java中翻墙等等。解决了全局的代理能完成我们访问到资源的时刻,又有新的要求,比方说:只有特定的资源走代理等等。

下面把要点简单罗列下,以供参考:

JDK官网基本内容全部都包括了,其他链接作为理解辅助,看看人家的实际需求与具体解决方法。

Java全应用级代理(全局)

  • 走HTTP

Shandowsocks转HTTP,前面Docker翻墙安装Kubernate有弄过,参考:Privoxy

也可以直接用Shandowsocks提供的 启用系统代理 -> 系统代理模式 -> 全局模式 来转换,启用HTTP代理功能。(开全局模式,本地会把socks代理转成为一个http的代理)

1
2
3
4
5
-Dhttp.proxyHost=127.0.0.1
-Dhttp.proxyPort=7070
-Dhttps.proxyHost=127.0.0.1
-Dhttps.proxyPort=7070
-Dhttp.nonProxyHosts="localhost|127.0.0.1|192.168.*"
  • http.proxyHost: the host name of the proxy server
  • http.proxyPort: the port number, the default value being 80.
  • http.nonProxyHosts:a list of hosts that should be reached directly, bypassing the proxy. This is a list of patterns separated by ‘|’. The patterns may start or end with a ‘*’ for wildcards. Any host matching one of these patterns will be reached through a direct connection instead of through a proxy.
  • 走Socks
1
-DsocksProxyHost=127.0.0.1 -DsocksProxyPort=7070
  • 使用系统代理
1
-Djava.net.useSystemProxies=true

部分/使用时设置(自动切换)

  • 应用内通过 setProperty 临时 进行设置(这种有缺陷,中间一段时间相当于全局代理了,不推荐)
1
2
3
4
System.setProperty("http.proxyHost", proxyHost);
System.setProperty("http.proxyPort", proxyPort);
System.setProperty("https.proxyHost", proxyHost);
System.setProperty("https.proxyPort", proxyPort);

用完之后,取消设置:

1
2
System.clearProperty("http.proxyHost");
...
  • 请求时指定代理:
1
2
3
4
5
SocketAddress addr = new InetSocketAddress("webcache.example.com", 8080);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);

URL url = new URL("http://java.example.org/");
URLConnection conn = url.openConnection(proxy);
  • (选择性的)配置哪些访问走代理:ProxySelector

任何请求访问网络之前,会被ProxySelector拦截。根据规则获取一个符合的Proxy(或者Proxy.NO_PROXY),然后通过这个代理去访问网络。

As you can see, with Java SE 5.0, the developer gains quite a bit of control and flexibility when it comes to proxies. Still, there are situations where one would like to decide which proxy to use dynamically, for instance to do some load balancing between proxies, or depending on the destination, in which case the API described so far would be quite cumbersome. That’s where the ProxySelector comes into play.

The best thing about the ProxySelector is that it is plugable! Which means that if you have needs that are not covered by the default one, you can write a replacement for it and plug it in!

基本上看JDK官网的内容就好了,非常全。也可以参考下 URLs and URIs, Proxies and Passwords

注册自定义的Selector:

1
2
3
4
5
public static void main(String[] args) {
    MyProxySelector ps = new MyProxySelector(ProxySelector.getDefault());
    ProxySelector.setDefault(ps);
    // rest of the application
}

Selector实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public class MyProxySelector extends ProxySelector {
    // Keep a reference on the previous default
    ProxySelector defsel = null;
    
    /*
     * Inner class representing a Proxy and a few extra data
     */
    class InnerProxy {
        Proxy proxy;
        SocketAddress addr;
        // How many times did we fail to reach this proxy?
        int failedCount = 0;
        
        InnerProxy(InetSocketAddress a) {
            this(a, Proxy.Type.HTTP);
        }
        
        InnerProxy(InetSocketAddress a, Proxy.Type type) {
            addr = a;
            proxy = new Proxy(type, a);
        }
        
        SocketAddress address() {
            return addr;
        }
        
        Proxy toProxy() {
            return proxy;
        }
        
        int failed() {
            return ++failedCount;
        }
    }
    
    /*
     * A list of proxies, indexed by their address.
     */
    HashMap<SocketAddress, InnerProxy> proxies = new HashMap<SocketAddress, InnerProxy>();

    MyProxySelector(ProxySelector def) {
        // Save the previous default
        defsel = def;
        
        // Populate the HashMap (List of proxies)
        InnerProxy i = new InnerProxy(new InetSocketAddress("webcache1.example.com", 8080));
        proxies.put(i.address(), i);
        i = new InnerProxy(new InetSocketAddress("webcache2.example.com", 8080));
        proxies.put(i.address(), i);
        i = new InnerProxy(new InetSocketAddress("webcache3.example.com", 8080));
        proxies.put(i.address(), i);
    }
        
    /*
     * This is the method that the handlers will call.
     * Returns a List of proxy.
     */
    public java.util.List<Proxy> select(URI uri) {
        // Let's stick to the specs. 
        if (uri == null) {
            throw new IllegalArgumentException("URI can't be null.");
        }
        
        /* 这里可以指定你自己的规则/配置
         * If it's a http (or https) URL, then we use our own list.
         */
        String protocol = uri.getScheme();
        if ("http".equalsIgnoreCase(protocol) ||
                "https".equalsIgnoreCase(protocol)) {
            ArrayList<Proxy> l = new ArrayList<Proxy>();
            for (InnerProxy p : proxies.values()) {
                l.add(p.toProxy());
            }
            return l;
        }
        
        /*
         * Not HTTP or HTTPS (could be SOCKS or FTP)
         * defer to the default selector.
         */
        if (defsel != null) {
            return defsel.select(uri);
        } else {
            ArrayList<Proxy> l = new ArrayList<Proxy>();
            l.add(Proxy.NO_PROXY);
            return l;
        }
    }
    
    /*
     * Method called by the handlers when it failed to connect
     * to one of the proxies returned by select().
     */
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        // Let's stick to the specs again.
        if (uri == null || sa == null || ioe == null) {
            throw new IllegalArgumentException("Arguments can't be null.");
        }
        
        /*
         * Let's lookup for the proxy 
         */
        InnerProxy p = proxies.get(sa); 
        if (p != null) {
            /*
             * It's one of ours, if it failed more than 3 times
             * let's remove it from the list.
             */
            if (p.failed() >= 3)
                    proxies.remove(sa);
        } else {
            /*
             * Not one of ours, let's delegate to the default.
             */
            if (defsel != null)
              defsel.connectFailed(uri, sa, ioe);
        }
    }
}

–END

Comments