├── README.md ├── netty-example-getway ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── wangzihaogithub │ │ └── getway │ │ ├── GetwayApplication10002.java │ │ └── controller │ │ └── MyController.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── netty-example-qps ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── netty │ └── example │ └── qps │ ├── OnceHttpTest.java │ ├── RunningHttpTest.java │ ├── RunningMQTTTest.java │ └── WebsocketTest.java ├── netty-example-rpc-api ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── nett │ └── example │ └── api │ └── HelloRpcApi.java ├── netty-example-rpc-consumer ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── netty │ │ └── example │ │ └── consumer │ │ ├── RpcConsumerApplication10000.java │ │ ├── api │ │ ├── HelloRpcController.java │ │ └── HelloRpcService.java │ │ └── controller │ │ ├── MyController.java │ │ └── RpcAop.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── netty-example-rpc-provider ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── netty │ │ └── example │ │ └── provider │ │ ├── RpcProviderApplication10001.java │ │ ├── controller │ │ └── HelloRpcController.java │ │ └── service │ │ ├── HelloRpcServiceImpl.java │ │ └── HelloStreamServiceImpl.java │ └── resources │ ├── application.yml │ └── bootstrap.yml ├── netty-example-servlet ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── netty │ │ └── example │ │ └── servlet │ │ └── ServletApplication10002.java │ └── resources │ └── application.yml ├── netty-example-sse-server ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── github │ │ └── netty │ │ └── example │ │ └── server │ │ ├── SseApplication10005.java │ │ └── controller │ │ ├── MyAccessUser.java │ │ ├── MySseWebController.java │ │ └── ServerSender.java │ └── resources │ ├── application.yml │ └── public │ └── index.html ├── netty-example-websocket-server ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── netty │ │ │ └── example │ │ │ └── server │ │ │ ├── WebsocketApplication10003.java │ │ │ └── controller │ │ │ └── WebsocketController.java │ └── resources │ │ ├── application.yml │ │ └── public │ │ └── index.html │ └── test │ └── java │ └── WebsocketTest.java ├── pom.xml └── spring-boot-example-protocol ├── pom.xml └── src └── main ├── java └── com │ └── github │ └── example │ ├── ProtocolApplication10004.java │ └── protocol │ ├── MyChannelHandler.java │ └── MyProtocol.java └── resources └── application.yml /README.md: -------------------------------------------------------------------------------- 1 | # netty-example 2 | 所有功能的使用示例 右键运行main方法即可 3 | 4 | --- 5 | 6 | 7 | #### 包 netty-example-qps 测试用途 8 | 9 | #### 包 spring-boot-example-protocol 主要功能!spring编写多协议示例代码 10 | 11 | #### 包 netty-example-rpc-api RPC接口定义jar包 12 | #### 包 netty-example-rpc-consumer RPC消费者示例代码 13 | #### 包 netty-example-rpc-provider RPC生产者示例代码 14 | 15 | #### 包 netty-example-servlet Servlet容器示例代码 16 | #### 包 netty-example-sse-server sse长连接示例代码 17 | #### 包 netty-example-websocket-server 长连接示例代码 18 | -------------------------------------------------------------------------------- /netty-example-getway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-getway 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-getway 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | 23 | com.github.wangzihaogithub 24 | netty-example-rpc-api 25 | 0.0.1-SNAPSHOT 26 | 27 | 28 | 29 | javax.servlet 30 | javax.servlet-api 31 | 4.0.1 32 | 33 | 34 | 35 | 36 | com.github.wangzihaogithub 37 | spring-boot-protocol 38 | 39 | 2.3.14 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-web 46 | 2.5.12 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-starter-gateway 57 | 2.1.2.RELEASE 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.springframework.cloud 73 | spring-cloud-starter 74 | 2.1.2.RELEASE 75 | 76 | 77 | 78 | org.apache.httpcomponents 79 | httpclient 80 | 4.5.13 81 | 82 | 83 | 84 | 85 | org.springframework.cloud 86 | spring-cloud-starter-alibaba-nacos-discovery 87 | 0.2.1.RELEASE 88 | 89 | 90 | com.alibaba.boot 91 | nacos-discovery-spring-boot-starter 92 | 0.2.1 93 | 94 | 95 | 96 | 97 | org.springframework.cloud 98 | spring-cloud-starter-alibaba-nacos-config 99 | 0.2.1.RELEASE 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-compiler-plugin 108 | 3.8.1 109 | 110 | ${java.version} 111 | ${java.version} 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /netty-example-getway/src/main/java/com/github/wangzihaogithub/getway/GetwayApplication10002.java: -------------------------------------------------------------------------------- 1 | package com.github.wangzihaogithub.getway; 2 | 3 | import com.github.netty.springboot.EnableNettyEmbedded; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.WebApplicationType; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | import org.springframework.cloud.gateway.route.Route; 9 | import org.springframework.web.reactive.config.EnableWebFlux; 10 | 11 | /** 12 | * RequestMappingHandlerAdapter 13 | * DispatcherHandler 14 | */ 15 | @EnableNettyEmbedded 16 | @EnableWebFlux 17 | @EnableDiscoveryClient 18 | @SpringBootApplication 19 | public class GetwayApplication10002 { 20 | 21 | public static void main(String[] args) { 22 | SpringApplication springApplication = new SpringApplication(GetwayApplication10002.class); 23 | springApplication.setWebApplicationType(WebApplicationType.REACTIVE); 24 | springApplication.run(args); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /netty-example-getway/src/main/java/com/github/wangzihaogithub/getway/controller/MyController.java: -------------------------------------------------------------------------------- 1 | package com.github.wangzihaogithub.getway.controller; 2 | 3 | import com.alibaba.nacos.api.annotation.NacosInjected; 4 | import com.alibaba.nacos.api.exception.NacosException; 5 | import com.alibaba.nacos.api.naming.NamingService; 6 | import com.alibaba.nacos.api.naming.pojo.Instance; 7 | import org.springframework.beans.factory.ObjectFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.cloud.context.config.annotation.RefreshScope; 11 | import org.springframework.core.io.buffer.DataBuffer; 12 | import org.springframework.http.HttpHeaders; 13 | import org.springframework.http.HttpMethod; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.http.client.reactive.ClientHttpResponse; 16 | import org.springframework.http.server.reactive.ServerHttpRequest; 17 | import org.springframework.stereotype.Controller; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.reactive.function.client.ClientResponse; 20 | import org.springframework.web.reactive.function.client.WebClient; 21 | import org.springframework.web.server.ServerWebExchange; 22 | import reactor.core.publisher.Flux; 23 | import reactor.core.publisher.Mono; 24 | 25 | import java.net.URI; 26 | import java.util.Objects; 27 | 28 | /** 29 | * @author wangzihao 30 | */ 31 | @Controller 32 | @RefreshScope 33 | public class MyController { 34 | @Value("${user.name:}") 35 | private String configCenterTest; 36 | @NacosInjected 37 | private NamingService namingService; 38 | @Autowired 39 | private ObjectFactory webClientBuilderFactory; 40 | 41 | @RequestMapping("/rpc/provider/**") 42 | public Mono>> rpcProvider(ServerWebExchange exchange) throws NacosException { 43 | Instance instance = namingService.selectOneHealthyInstance("rpc-provider-service"); 44 | Mono>> asyncResponse = asyncRequest(exchange.getRequest(), instance); 45 | return asyncResponse; 46 | } 47 | 48 | @RequestMapping("/rpc/consumer/**") 49 | public Mono>> rpcConsumer(ServerWebExchange exchange) throws NacosException { 50 | Instance instance = namingService.selectOneHealthyInstance("rpc-consumer-service"); 51 | Mono>> asyncResponse = asyncRequest(exchange.getRequest(), instance); 52 | return asyncResponse; 53 | } 54 | 55 | private Mono>> asyncRequest(ServerHttpRequest httpRequest, Instance instance) throws NacosException { 56 | String rawProtocol = httpRequest.getSslInfo() == null ? "http://" : "https://"; 57 | HttpMethod rawHttpMethod = Objects.requireNonNull(httpRequest.getMethod()); 58 | URI rawUri = httpRequest.getURI(); 59 | HttpHeaders rawHeaders = httpRequest.getHeaders(); 60 | Flux rawBody = httpRequest.getBody(); 61 | 62 | String url = rawProtocol + instance.getIp() + ":" + instance.getPort() + 63 | rawUri.getRawPath() + "?" + rawUri.getRawQuery(); 64 | WebClient.Builder webClientBuilder = webClientBuilderFactory.getObject() 65 | .baseUrl(url); 66 | 67 | Mono exchangeMono = webClientBuilder.build() 68 | .method(rawHttpMethod) 69 | .headers((HttpHeaders httpHeaders) -> { 70 | httpHeaders.clear(); 71 | httpHeaders.addAll(rawHeaders); 72 | }) 73 | .body(rawBody, DataBuffer.class) 74 | .exchange(); 75 | 76 | Mono>> clientResponseMono = exchangeMono.map((ClientResponse clientResponse) -> { 77 | ClientHttpResponse clientHttpResponse = clientResponse.body((httpResponse, context) -> httpResponse); 78 | return new ResponseEntity<>( 79 | clientHttpResponse.getBody(), 80 | clientResponse.headers().asHttpHeaders(), 81 | clientResponse.statusCode() 82 | ); 83 | }); 84 | return clientResponseMono; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /netty-example-getway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10002 3 | netty: 4 | enable-tcp-package-log: true 5 | max-connections: 10000 6 | logging: 7 | level: 8 | com.github.netty: debug 9 | org: 10 | apache: 11 | tomcat: debug 12 | coyote: debug -------------------------------------------------------------------------------- /netty-example-getway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: getway-service 4 | profiles: 5 | active: test 6 | cloud: 7 | nacos: 8 | discovery: 9 | server-addr: 127.0.0.1:8848 10 | config: 11 | server-addr: 127.0.0.1:8848 12 | file-extension: yaml 13 | 14 | nacos: 15 | server-addr: 127.0.0.1:8848 -------------------------------------------------------------------------------- /netty-example-qps/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-qps 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-qps 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | 24 | 2.1.4.RELEASE 25 | 26 | 27 | 28 | 29 | 30 | 31 | io.vertx 32 | vertx-web-client 33 | 3.5.0.Beta1 34 | 35 | 36 | 37 | 38 | io.vertx 39 | vertx-mqtt 40 | 3.5.0.Beta1 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-websocket 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-compiler-plugin 55 | 3.8.1 56 | 57 | ${java.version} 58 | ${java.version} 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /netty-example-qps/src/main/java/com/github/netty/example/qps/OnceHttpTest.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.qps; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.json.JsonObject; 5 | import io.vertx.ext.web.client.WebClient; 6 | import io.vertx.ext.web.client.WebClientOptions; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.math.BigDecimal; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * 一次qps统计 (一次运行) 注: servlet服务端口=10002,rpc-consumer服务端口=10000,rpc-provider服务端口=10001 17 | * 18 | * 用于测试qps性能, 直接右键运行即可 19 | * Http协议 20 | * @author acer01 21 | * 2018/8/12/012 22 | */ 23 | public class OnceHttpTest { 24 | private static final int PORT = 10002; 25 | private static final String HOST = "localhost"; 26 | private static final String URI = "/hello?id=1&name=abc"; 27 | private static final JsonObject BODY = new JsonObject("{\"body1\":\"QpsOnceTest-我是post内容\"}"); 28 | 29 | private int queryCount = 10000;//===========总调用次数================= 30 | private int waitTime = 10;//===========等待时间(秒)================= 31 | private AtomicInteger successCount = new AtomicInteger(); 32 | private AtomicInteger errorCount = new AtomicInteger(); 33 | private CountDownLatch latch = new CountDownLatch(queryCount); 34 | private long totalTime; 35 | 36 | //==============Vertx客户端=============== 37 | private Vertx vertx = Vertx.vertx(); 38 | private WebClient client = WebClient.create(vertx, new WebClientOptions() 39 | .setTcpKeepAlive(false) 40 | //是否保持连接 41 | .setKeepAlive(true)); 42 | 43 | public static void main(String[] args) throws InterruptedException { 44 | OnceHttpTest test = new OnceHttpTest(); 45 | test.doQuery(PORT, HOST, URI); 46 | 47 | int successCount = test.successCount.get(); 48 | int errorCount = test.errorCount.get(); 49 | long totalTime = test.totalTime; 50 | 51 | test.client.close(); 52 | test.vertx.close(); 53 | 54 | if (successCount == 0) { 55 | logger.info("无成功调用"); 56 | } else { 57 | logger.info("时间 = " + totalTime + "毫秒, " + 58 | "成功 = " + successCount + ", " + 59 | "失败 = " + errorCount + ", " + 60 | "qps = " + new BigDecimal((double) successCount / (double) totalTime * 1000).setScale(2, BigDecimal.ROUND_HALF_DOWN).stripTrailingZeros().toPlainString() + 61 | "\r\n===============================" 62 | ); 63 | } 64 | } 65 | 66 | private void doQuery(int port, String host, String uri) throws InterruptedException { 67 | long beginTime = System.currentTimeMillis(); 68 | for (int i = 0; i < queryCount; i++) { 69 | client.post(port, host, uri).sendJsonObject(BODY, asyncResult -> { 70 | if (asyncResult.succeeded()) { 71 | successCount.incrementAndGet(); 72 | } else { 73 | errorCount.incrementAndGet(); 74 | System.out.println("error = " + asyncResult.cause()); 75 | } 76 | latch.countDown(); 77 | }); 78 | } 79 | latch.await(waitTime, TimeUnit.SECONDS); 80 | totalTime = System.currentTimeMillis() - beginTime; 81 | } 82 | 83 | 84 | static final Logger logger = LoggerFactory.getLogger(OnceHttpTest.class); 85 | 86 | } 87 | -------------------------------------------------------------------------------- /netty-example-qps/src/main/java/com/github/netty/example/qps/RunningHttpTest.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.qps; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.json.JsonObject; 5 | import io.vertx.ext.web.client.WebClient; 6 | import io.vertx.ext.web.client.WebClientOptions; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.math.BigDecimal; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | import java.util.concurrent.atomic.AtomicLong; 15 | 16 | /** 17 | * running 测试 (一直运行) 注: servlet服务端口=10002,rpc-consumer服务端口=10000,rpc-provider服务端口=10001 18 | *

19 | * 用于测试qps性能, 直接右键运行即可 20 | * Http协议 21 | * 22 | * @author acer01 23 | * 2018/8/12/012 24 | */ 25 | public class RunningHttpTest { 26 | private static final int PORT = 10002; 27 | private static final String HOST = "localhost"; 28 | private static final String URI = "/hello?id=1&name=abc"; 29 | private static final JsonObject BODY = new JsonObject("{\"body1\":\"QpsRunningTest-我是post内容\"}"); 30 | 31 | private int queryCount = 10000;//===========一次qps任务的调用次数================= 32 | private int waitTime = 10;//===========一次qps任务的等待时间(秒)================= 33 | 34 | private static final long reportPrintTime = 5;//===========qps统计间隔时间(秒)================= 35 | private static final long onceSleep = 300;//===========下次调用qps任务的暂停时间(毫秒)================= 36 | 37 | private AtomicInteger successCount = new AtomicInteger(); 38 | private AtomicInteger errorCount = new AtomicInteger(); 39 | private AtomicLong totalSleepTime = new AtomicLong(); 40 | 41 | //==============Vertx客户端=============== 42 | private WebClient client = WebClient.create(Vertx.vertx(), new WebClientOptions() 43 | .setTcpKeepAlive(false) 44 | //是否保持连接 45 | .setKeepAlive(true)); 46 | 47 | public static void main(String[] args) { 48 | RunningHttpTest test = new RunningHttpTest(); 49 | new PrintThread(test).start(); 50 | 51 | try { 52 | while (true) { 53 | test.doQuery(PORT, HOST, URI); 54 | } 55 | } catch (Throwable t) { 56 | t.printStackTrace(); 57 | } 58 | } 59 | 60 | private void doQuery(int port, String host, String uri) throws InterruptedException { 61 | CountDownLatch latch = new CountDownLatch(queryCount); 62 | for (int i = 0; i < queryCount; i++) { 63 | client.get(port, host, uri).sendJsonObject(BODY, asyncResult -> { 64 | if (asyncResult.succeeded()) { 65 | successCount.incrementAndGet(); 66 | } else { 67 | errorCount.incrementAndGet(); 68 | System.out.println("error = " + asyncResult.cause()); 69 | } 70 | latch.countDown(); 71 | }); 72 | } 73 | 74 | latch.await(waitTime, TimeUnit.SECONDS); 75 | Thread.sleep(onceSleep); 76 | totalSleepTime.addAndGet(onceSleep); 77 | } 78 | 79 | static class PrintThread extends Thread { 80 | private final RunningHttpTest test; 81 | private AtomicInteger printCount = new AtomicInteger(); 82 | private long beginTime = System.currentTimeMillis(); 83 | static final Logger logger = LoggerFactory.getLogger(PrintThread.class); 84 | 85 | PrintThread(RunningHttpTest test) { 86 | super("QpsPrintThread"); 87 | this.test = test; 88 | } 89 | 90 | @Override 91 | public void run() { 92 | while (true) { 93 | try { 94 | sleep(reportPrintTime * 1000); 95 | // synchronized (test) { 96 | long totalTime = System.currentTimeMillis() - beginTime - test.totalSleepTime.get(); 97 | printQps(test.successCount.get(), test.errorCount.get(), totalTime); 98 | // } 99 | } catch (Throwable t) { 100 | t.printStackTrace(); 101 | } 102 | } 103 | } 104 | 105 | private void printQps(int successCount, int errorCount, long totalTime) { 106 | if (successCount == 0) { 107 | logger.info("无成功调用"); 108 | } else { 109 | logger.info( 110 | "第(" + printCount.incrementAndGet() + ")次统计, " + 111 | "时间 = " + totalTime + "毫秒[" + (totalTime / 60000) + "分" + ((totalTime % 60000) / 1000) + "秒], " + 112 | "成功 = " + successCount + ", " + 113 | "失败 = " + errorCount + ", " + 114 | "平均响应 = " + new BigDecimal((double) totalTime / (double) successCount).setScale(2, BigDecimal.ROUND_HALF_DOWN).stripTrailingZeros().toPlainString() + "ms, " + 115 | "qps = " + new BigDecimal((double) successCount / (double) totalTime * 1000).setScale(2, BigDecimal.ROUND_HALF_DOWN).stripTrailingZeros().toPlainString() 116 | // + 117 | // "\r\n===============================" 118 | ); 119 | } 120 | } 121 | } 122 | 123 | 124 | } 125 | -------------------------------------------------------------------------------- /netty-example-qps/src/main/java/com/github/netty/example/qps/RunningMQTTTest.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.qps; 2 | 3 | import io.netty.handler.codec.mqtt.MqttQoS; 4 | import io.vertx.core.AbstractVerticle; 5 | import io.vertx.core.Verticle; 6 | import io.vertx.core.Vertx; 7 | import io.vertx.core.buffer.Buffer; 8 | import io.vertx.mqtt.MqttClient; 9 | import io.vertx.mqtt.MqttClientOptions; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.math.BigDecimal; 14 | import java.nio.charset.Charset; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.StringJoiner; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | /** 25 | * running 测试 (一直运行) 注: mqtt服务端口=10004 26 | *

27 | * 用于测试qps性能, 直接右键运行即可 28 | * MQTT协议 29 | * 30 | * @author acer01 31 | * 2018/8/12/012 32 | */ 33 | public class RunningMQTTTest { 34 | private static Logger logger = LoggerFactory.getLogger(RunningMQTTTest.class); 35 | private static final int PORT = 10004; 36 | private static final String HOST = "localhost"; 37 | private static final String TOPIC = 38 | "#"; 39 | // "/hello?id=1&name=abc"; 40 | 41 | private static final long reportPrintTime = 5;//===========统计间隔时间(秒)================= 42 | private static AtomicInteger successCount = new AtomicInteger(); 43 | private static AtomicInteger errorCount = new AtomicInteger(); 44 | private static AtomicLong totalSleepTime = new AtomicLong(); 45 | 46 | 47 | //==============Vertx客户端=============== 48 | 49 | public static void main(String[] args) { 50 | ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2); 51 | Verticle verticle = new AbstractVerticle() { 52 | @Override 53 | public void start() { 54 | MqttClient client = MqttClient.create(vertx, new MqttClientOptions() 55 | .setHost(HOST) 56 | .setPort(PORT) 57 | .setWillTopic("willTopic") 58 | .setWillMessage("hello") 59 | .setWillFlag(true) 60 | .setUsername("admin") 61 | .setPassword("123456") 62 | .setMaxMessageSize(8192)); 63 | 64 | client.publishHandler(response -> { 65 | String message = new String(response.payload().getBytes(), Charset.forName("UTF-8")); 66 | logger.info("接收到消息: {} from topic {}", message, response.topicName()); 67 | }); 68 | 69 | client.connect(s -> { 70 | if (!s.succeeded()) { 71 | vertx.close(); 72 | scheduled.shutdown(); 73 | return; 74 | } 75 | 76 | Map topics = new HashMap<>(2); 77 | topics.put(TOPIC, MqttQoS.AT_LEAST_ONCE.value()); 78 | // subscribe to all subtopics 79 | client.subscribe(topics, resp -> { 80 | int result = resp.result(); 81 | logger.info("subscribe {}", resp); 82 | }); 83 | 84 | 85 | AtomicInteger count = new AtomicInteger(); 86 | scheduled.scheduleAtFixedRate(() -> 87 | client.publish("/hello", 88 | Buffer.buffer("发布数据" + count.incrementAndGet()), MqttQoS.EXACTLY_ONCE, true, true, 89 | asyncResult -> { 90 | if (asyncResult.succeeded()) { 91 | // logger.info("publish {}",asyncResult); 92 | } 93 | } 94 | ), 0, 15, TimeUnit.MILLISECONDS); 95 | }); 96 | } 97 | }; 98 | 99 | Vertx.vertx().deployVerticle(verticle); 100 | scheduled.scheduleAtFixedRate(new PrintInfoRunnable(), 0, reportPrintTime, TimeUnit.SECONDS); 101 | } 102 | 103 | 104 | static class PrintInfoRunnable implements Runnable { 105 | private AtomicInteger printCount = new AtomicInteger(); 106 | private long beginTime = System.currentTimeMillis(); 107 | 108 | @Override 109 | public void run() { 110 | long totalTime = System.currentTimeMillis() - beginTime - totalSleepTime.get(); 111 | int successCount = RunningMQTTTest.successCount.get(); 112 | int errorCount = RunningMQTTTest.errorCount.get(); 113 | 114 | StringJoiner joiner = new StringJoiner(","); 115 | joiner.add("第(" + printCount.incrementAndGet() + ")次统计 "); 116 | joiner.add("时间 = " + totalTime + "毫秒[" + (totalTime / 60000) + "分" + ((totalTime % 60000) / 1000) + "秒] "); 117 | joiner.add("成功 = " + successCount); 118 | joiner.add("失败 = " + errorCount); 119 | joiner.add("平均响应 = " + new BigDecimal((double) totalTime / (double) successCount) 120 | .setScale(2, BigDecimal.ROUND_HALF_DOWN).stripTrailingZeros().toPlainString() + "ms, "); 121 | joiner.add("qps = " + new BigDecimal((double) successCount / (double) totalTime * 1000) 122 | .setScale(2, BigDecimal.ROUND_HALF_DOWN).stripTrailingZeros().toPlainString()); 123 | 124 | logger.info(joiner.toString()); 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /netty-example-qps/src/main/java/com/github/netty/example/qps/WebsocketTest.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.qps; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.messaging.converter.MappingJackson2MessageConverter; 6 | import org.springframework.messaging.simp.stomp.StompHeaders; 7 | import org.springframework.messaging.simp.stomp.StompSession; 8 | import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; 9 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 10 | import org.springframework.web.socket.WebSocketHttpHeaders; 11 | import org.springframework.web.socket.client.standard.StandardWebSocketClient; 12 | import org.springframework.web.socket.messaging.WebSocketStompClient; 13 | import org.springframework.web.socket.sockjs.client.SockJsClient; 14 | import org.springframework.web.socket.sockjs.client.WebSocketTransport; 15 | 16 | import java.util.*; 17 | import java.util.concurrent.CopyOnWriteArrayList; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | /** 24 | * 客户端测试 ( Websocket服务端口 : 10003) 25 | * 从Http协议协商至Websocket STOMP协议 26 | * 27 | * @author wangzihao 28 | */ 29 | public class WebsocketTest { 30 | 31 | public static void main(String[] args) { 32 | ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(3); 33 | //发起连接的次数 34 | AtomicInteger connectCount = new AtomicInteger(); 35 | //连接成功数 36 | AtomicInteger successCount = new AtomicInteger(); 37 | //连接失败数 38 | AtomicInteger errorCount = new AtomicInteger(); 39 | //链接的列表 40 | List sessionList = new CopyOnWriteArrayList<>(); 41 | //订阅的列表 42 | List subscriptionList = new CopyOnWriteArrayList<>(); 43 | 44 | //连接并订阅 45 | String url = "ws://localhost:10003/my-websocket?access-token=b90b0e77-63cf-4b05-8d8b-43ebefc71a6a"; 46 | Runnable connectRunnable = newConnectAndSubscribeRunnable(url, connectCount, successCount, errorCount, sessionList, subscriptionList); 47 | scheduledService.scheduleAtFixedRate(connectRunnable, 0, 1000, TimeUnit.MILLISECONDS);//1秒间隔 一次新连接 48 | 49 | //发送消息 50 | Runnable sendMessageRunnable = newSendMessageRunnable(sessionList); 51 | scheduledService.scheduleAtFixedRate(sendMessageRunnable, 0, 1000, TimeUnit.MILLISECONDS);//1秒间隔 所有会话发送消息 52 | 53 | scheduledService.scheduleAtFixedRate(() -> { 54 | //每次5 秒打印一次详情 55 | logger.info(" 连接数:" + connectCount + " 成功数:" + successCount + " 失败数:" + errorCount); 56 | }, 5, 5, TimeUnit.SECONDS); 57 | } 58 | 59 | /** 60 | * 发送消息 61 | * 62 | * @param sessionList 63 | * @return 64 | */ 65 | private static Runnable newSendMessageRunnable(List sessionList) { 66 | return new Runnable() { 67 | @Override 68 | public void run() { 69 | int i = 0; 70 | for (StompSession session : sessionList) { 71 | i++; 72 | 73 | StompHeaders headers = new StompHeaders(); 74 | headers.setDestination("/app/receiveMessage"); 75 | headers.set("my-login-user", "小" + i); 76 | 77 | Map payload = new HashMap<>(2); 78 | payload.put("msg", "你好"); 79 | 80 | session.send(headers, payload); 81 | } 82 | } 83 | }; 84 | } 85 | 86 | private static Logger logger = LoggerFactory.getLogger(WebsocketTest.class); 87 | private static ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); 88 | private static MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter(); 89 | private static WebSocketStompClient client = new WebSocketStompClient(new SockJsClient(Arrays.asList(new WebSocketTransport(new StandardWebSocketClient())))); 90 | 91 | static { 92 | taskScheduler.afterPropertiesSet(); 93 | client.setMessageConverter(messageConverter); 94 | client.setTaskScheduler(taskScheduler); 95 | } 96 | 97 | /** 98 | * 连接 并且 订阅一个主题 99 | * 100 | * @param url 101 | * @param connectCount 102 | * @param successCount 103 | * @param errorCount 104 | * @param connectList 105 | * @return 106 | */ 107 | private static Runnable newConnectAndSubscribeRunnable(String url, AtomicInteger connectCount, AtomicInteger successCount, AtomicInteger errorCount, 108 | List connectList, List subscriptionList) { 109 | return new Runnable() { 110 | int i = 0; 111 | Random random = new Random(); 112 | 113 | @Override 114 | public void run() { 115 | try { 116 | WebSocketHttpHeaders httpHeaders = new WebSocketHttpHeaders(); 117 | String accessToken = "user" + i; 118 | httpHeaders.add("access-token", accessToken); 119 | //连接至url 120 | StompSession session = client.connect(url, httpHeaders, new StompSessionHandlerAdapter() { 121 | @Override 122 | public void afterConnected(StompSession session, StompHeaders headers) { 123 | successCount.incrementAndGet(); 124 | } 125 | 126 | @Override 127 | public void handleTransportError(StompSession session, Throwable exception) { 128 | errorCount.incrementAndGet(); 129 | } 130 | }).get(); 131 | connectList.add(session); 132 | 133 | //订阅一个主题 134 | String destination = "/app/user/room/user" + random.nextInt(connectList.size()) + "/room" + random.nextInt(connectList.size()); 135 | StompSession.Subscription subscription = session.subscribe(destination, new StompSessionHandlerAdapter() { 136 | }); 137 | subscriptionList.add(subscription); 138 | 139 | i++; 140 | } catch (Exception e) { 141 | // 142 | } finally { 143 | connectCount.incrementAndGet(); 144 | } 145 | } 146 | }; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /netty-example-rpc-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.wangzihaogithub 9 | netty-example-rpc-api 10 | 0.0.1-SNAPSHOT 11 | 12 | 13 | UTF-8 14 | UTF-8 15 | 1.8 16 | 2.5.12 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | ${spring-boot.version} 24 | compile 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-compiler-plugin 34 | 3.8.1 35 | 36 | ${java.version} 37 | ${java.version} 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /netty-example-rpc-api/src/main/java/com/github/nett/example/api/HelloRpcApi.java: -------------------------------------------------------------------------------- 1 | package com.github.nett.example.api; 2 | 3 | import org.springframework.web.bind.annotation.RequestBody; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RequestParam; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * @author wangzihao 11 | */ 12 | @RequestMapping("/rpc/provider/service")//这里的值要与 生产者(服务端)对应 13 | public interface HelloRpcApi { 14 | 15 | /** 16 | * hello world 17 | * @param body 18 | * @param name 19 | * @param id 20 | * @param pwd 21 | * @return 22 | */ 23 | Map helloService(@RequestBody Map body, @RequestParam("name") String name, @RequestParam("id") Integer id, @RequestParam("pwd") String pwd);//方法名称与 生产者(服务端)对应 24 | 25 | } -------------------------------------------------------------------------------- /netty-example-rpc-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-rpc-consumer 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-rpc-consumer 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | 23 | com.github.wangzihaogithub 24 | netty-example-rpc-api 25 | 0.0.1-SNAPSHOT 26 | 27 | 28 | 29 | 30 | com.github.wangzihaogithub 31 | spring-boot-protocol 32 | 2.3.14 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 2.5.12 40 | 41 | 42 | 43 | 44 | com.alibaba.nacos 45 | nacos-client 46 | 2.1.2 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-compiler-plugin 55 | 3.8.1 56 | 57 | ${java.version} 58 | ${java.version} 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/java/com/github/netty/example/consumer/RpcConsumerApplication10000.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.consumer; 2 | 3 | import com.alibaba.nacos.api.exception.NacosException; 4 | import com.alibaba.nacos.api.naming.NamingFactory; 5 | import com.alibaba.nacos.api.naming.NamingService; 6 | import com.alibaba.nacos.api.naming.pojo.Instance; 7 | import com.github.netty.springboot.EnableNettyEmbedded; 8 | import com.github.netty.springboot.EnableNettyRpcClients; 9 | import com.github.netty.springboot.client.NettyRpcLoadBalanced; 10 | import com.github.netty.springboot.client.NettyRpcRequest; 11 | import org.springframework.boot.SpringApplication; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.net.InetSocketAddress; 16 | 17 | /** 18 | * RPC消费者 端口号:10000, 远程端口号: 10001 19 | * Netty-rpc 适用于数据量小(例: 2M左右), 并发高的场景 20 | * 21 | * @author wangzihao (示例) 22 | */ 23 | @EnableNettyEmbedded//切换容器的注解, 可选不切换, 继续用tomcat 24 | @SpringBootApplication 25 | @EnableNettyRpcClients//这里开启自动注入RPC服务功能 26 | public class RpcConsumerApplication10000 { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(RpcConsumerApplication10000.class, args); 30 | } 31 | 32 | /** 33 | * 寻找地址, 需要用户自行根据需求实现 (这里用 nacos实现) 34 | * 35 | * @author wangzihao 36 | */ 37 | @Component 38 | public static class NacosRpcLoadBalanced implements NettyRpcLoadBalanced { 39 | private NamingService namingService; 40 | private boolean namingServiceFail; 41 | 42 | public synchronized NamingService getNamingService() throws NacosException { 43 | if (namingService == null) { 44 | namingService = NamingFactory.createNamingService("127.0.0.1:8848"); 45 | } 46 | return namingService; 47 | } 48 | 49 | @Override 50 | public InetSocketAddress chooseAddress(NettyRpcRequest request) { 51 | InetSocketAddress inetSocketAddress; 52 | if (namingServiceFail) { 53 | inetSocketAddress = new InetSocketAddress("localhost", 10001); 54 | } else { 55 | try { 56 | Instance instance = getNamingService().selectOneHealthyInstance(request.getServiceName()); 57 | inetSocketAddress = new InetSocketAddress(instance.getIp(), instance.getPort()); 58 | } catch (Exception e) { 59 | namingServiceFail = true; 60 | inetSocketAddress = new InetSocketAddress("localhost", 10001); 61 | } 62 | } 63 | return inetSocketAddress; 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/java/com/github/netty/example/consumer/api/HelloRpcController.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.consumer.api; 2 | 3 | import com.github.netty.springboot.NettyRpcClient; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RequestParam; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * hello world 11 | * 12 | * @author wangzihao 13 | */ 14 | @NettyRpcClient(serviceName = "rpc-provider-service") 15 | //这里的名称会传入NettyRpcLoadBalanced.class的chooseAddress方法,由chooseAddress方法提供IP地址.该方法由消费者自行实现(例如: eureka zokeeper, nacos) 16 | @RequestMapping("/rpc/provider/controller")//这里的值要与服务端的值一致 17 | public interface HelloRpcController { 18 | 19 | /** 20 | * hello world 21 | * 22 | * @param name 23 | * @param id 24 | * @param pwd 25 | * @return 26 | */ 27 | Map helloController(@RequestParam("name") String name, @RequestParam("id") Integer id, @RequestParam("pwd") String pwd);//方法名称与 生产者(服务端)对应 28 | 29 | } -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/java/com/github/netty/example/consumer/api/HelloRpcService.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.consumer.api; 2 | 3 | import com.github.nett.example.api.HelloRpcApi; 4 | import com.github.netty.springboot.NettyRpcClient; 5 | 6 | /** 7 | * hello world 8 | * 9 | * @author wangzihao 10 | */ 11 | @NettyRpcClient(serviceName = "rpc-provider-service") 12 | //这里的名称会传入NettyRpcLoadBalanced.class的chooseAddress方法,由chooseAddress方法提供IP地址.该方法由消费者自行实现(例如: eureka zokeeper) 13 | public interface HelloRpcService extends HelloRpcApi { 14 | 15 | } -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/java/com/github/netty/example/consumer/controller/MyController.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.consumer.controller; 2 | 3 | import com.github.netty.example.consumer.api.HelloRpcController; 4 | import com.github.netty.example.consumer.api.HelloRpcService; 5 | import com.github.netty.protocol.nrpc.RpcClientChunkCompletableFuture; 6 | import com.github.netty.springboot.NettyRpcClient; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.context.request.async.DeferredResult; 12 | 13 | import javax.annotation.Resource; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.util.LinkedHashMap; 17 | import java.util.Map; 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | /** 21 | * @author wangzihao 22 | */ 23 | @RestController 24 | @RequestMapping("/rpc/consumer") 25 | public class MyController { 26 | @Resource 27 | private HelloRpcService helloRpcService; 28 | @Resource 29 | private HelloRpcController helloRpcController; 30 | @Resource 31 | private HelloRpcAsyncClient helloRpcAsyncClient; 32 | @Resource 33 | private HelloRpcStreamClient helloRpcStreamClient; 34 | 35 | @RequestMapping("/callProvider") 36 | public DeferredResult callProvider(@RequestParam Map query, @RequestBody(required = false) Map body, 37 | HttpServletRequest request, HttpServletResponse response) { 38 | DeferredResult deferredResult = new DeferredResult<>(); 39 | 40 | String name = (String) query.get("name"); 41 | Map map = new LinkedHashMap<>(); 42 | map.put("call-service", helloRpcService.helloService(query, name, 1, "Test")); 43 | map.put("call-controller", helloRpcController.helloController(name, 1, "Test")); 44 | helloRpcAsyncClient.helloService(query, name, 1, "Test").whenComplete((map1, throwable) -> { 45 | map.put("call-service-async", map1); 46 | 47 | Map streamData = new LinkedHashMap<>(); 48 | map.put("call-stream", streamData); 49 | helloRpcStreamClient.helloStream(query, name, 2, "Test").whenChunk((chunk, index) -> { 50 | streamData.put(index + "", chunk); 51 | }).whenComplete((complete, throwable1) -> { 52 | streamData.put("complete", complete); 53 | streamData.put("throwable", throwable1); 54 | deferredResult.setResult(map); 55 | }); 56 | }); 57 | return deferredResult; 58 | } 59 | 60 | @NettyRpcClient(serviceName = "rpc-provider-service") 61 | @RequestMapping("/rpc/provider/service")//这里的值要与 生产者(服务端)对应 62 | public interface HelloRpcAsyncClient { 63 | CompletableFuture helloService(@RequestBody Map body, @RequestParam("name") String name, @RequestParam("id") Integer id, @RequestParam("pwd") String pwd);//方法名称与 生产者(服务端)对应 64 | } 65 | 66 | @NettyRpcClient(serviceName = "rpc-provider-service") 67 | @RequestMapping("/rpc/provider/stream")//这里的值要与 生产者(服务端)对应 68 | public interface HelloRpcStreamClient { 69 | RpcClientChunkCompletableFuture helloStream(@RequestBody Map body, @RequestParam("name") String name, @RequestParam("id") Integer id, @RequestParam("pwd") String pwd);//方法名称与 生产者(服务端)对应 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/java/com/github/netty/example/consumer/controller/RpcAop.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.consumer.controller; 2 | 3 | import com.github.netty.protocol.NRpcProtocol; 4 | import com.github.netty.protocol.nrpc.*; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * rpc调用的生命周期管理 11 | * @author wangzihao 2019年12月7日16:33:43 12 | */ 13 | @Component 14 | public class RpcAop implements RpcServerAop { 15 | @Override 16 | public void onInitAfter(NRpcProtocol protocol) { 17 | System.out.println("rpcContext = " + protocol); 18 | } 19 | 20 | @Override 21 | public void onConnectAfter(RpcServerChannelHandler channel) { 22 | System.out.println("rpcContext = " + channel); 23 | } 24 | 25 | @Override 26 | public void onDisconnectAfter(RpcServerChannelHandler channel) { 27 | System.out.println("rpcContext = " + channel); 28 | } 29 | 30 | @Override 31 | public void onDecodeRequestBefore(RpcContext rpcContext, Map params) { 32 | System.out.println("rpcContext = " + rpcContext); 33 | } 34 | 35 | @Override 36 | public void onResponseAfter(RpcContext rpcContext) { 37 | System.out.println("rpcContext = " + rpcContext); 38 | } 39 | 40 | @Override 41 | public void onStateUpdate(RpcContext rpcContext, State formState, State toState) { 42 | System.out.println("onStateUpdate = " + toState); 43 | } 44 | } -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10000 3 | netty: 4 | enable-tcp-package-log: false 5 | max-connections: 10000 6 | logging: 7 | level: 8 | com.github.netty: debug -------------------------------------------------------------------------------- /netty-example-rpc-consumer/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: rpc-consumer-service 4 | profiles: 5 | active: test -------------------------------------------------------------------------------- /netty-example-rpc-provider/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-rpc-provider 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-rpc-provider 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 2.1.0.RELEASE 19 | 2.0.0.RELEASE 20 | 2.0.0.RELEASE 21 | 22 | 23 | 24 | 25 | 26 | com.github.wangzihaogithub 27 | netty-example-rpc-api 28 | 0.0.1-SNAPSHOT 29 | 30 | 31 | 32 | 33 | com.github.wangzihaogithub 34 | spring-boot-protocol 35 | 2.3.14 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 2.5.12 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-compiler-plugin 51 | 3.8.1 52 | 53 | ${java.version} 54 | ${java.version} 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /netty-example-rpc-provider/src/main/java/com/github/netty/example/provider/RpcProviderApplication10001.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.provider; 2 | 3 | import com.github.netty.springboot.EnableNettyEmbedded; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * RPC生产者 端口号:10001 可以单端口同时使用多协议,例:80端口[http,rpc,mqtt,ftp] (这样依赖spring) 9 | * Netty-rpc 适用于数据量小(例: 2M左右), 并发高的场景 10 | * 11 | * @author wangzihao 12 | */ 13 | @EnableNettyEmbedded//这里需要切换为netty容器 14 | @SpringBootApplication 15 | public class RpcProviderApplication10001 { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(RpcProviderApplication10001.class, args); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /netty-example-rpc-provider/src/main/java/com/github/netty/example/provider/controller/HelloRpcController.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.provider.controller; 2 | 3 | import com.github.netty.annotation.NRpcService; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author wangzihao 14 | */ 15 | @NRpcService//启用服务端的RPC 16 | @RequestMapping("/rpc/provider/controller") 17 | @RestController 18 | public class HelloRpcController { 19 | 20 | @RequestMapping("/helloController") 21 | public Map helloController(String name, Integer id, String pwd, HttpServletResponse response) throws IOException, InterruptedException { 22 | // response.setHeader("Content-Type", "application/json"); 23 | // int i = 0; 24 | // while (i< 100){ 25 | // i++; 26 | // Thread.sleep(1000); 27 | // response.getWriter().write(i + "你好"); 28 | // response.flushBuffer(); 29 | // } 30 | Map map = new HashMap(1); 31 | map.put("msg", "helloController (" + name + "," + id + "," + pwd + ") " + System.currentTimeMillis()); 32 | return map; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /netty-example-rpc-provider/src/main/java/com/github/netty/example/provider/service/HelloRpcServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.provider.service; 2 | 3 | import com.github.nett.example.api.HelloRpcApi; 4 | import com.github.netty.annotation.NRpcService; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author wangzihao 11 | */ 12 | @NRpcService//启用服务端的RPC 13 | public class HelloRpcServiceImpl implements HelloRpcApi { 14 | 15 | @Override 16 | public Map helloService(Map body, String name, Integer id, String pwd) { 17 | Map map = new HashMap(1); 18 | map.put("msg", "helloService (" + body + "," + name + "," + id + "," + pwd + ") " + System.currentTimeMillis()); 19 | return map; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /netty-example-rpc-provider/src/main/java/com/github/netty/example/provider/service/HelloStreamServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.provider.service; 2 | 3 | import com.github.netty.annotation.NRpcService; 4 | import com.github.netty.protocol.nrpc.RpcEmitter; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author wangzihao 11 | */ 12 | @NRpcService("/rpc/provider/stream")//启用服务端的RPC 13 | public class HelloStreamServiceImpl { 14 | 15 | public RpcEmitter helloStream(Map requestBody, String name, Integer id, String pwd) { 16 | RpcEmitter emitter = new RpcEmitter<>(); 17 | for (int i = 10; i > 0; i--) { 18 | emitter.send("chunk" + i); 19 | } 20 | new Thread(() -> { 21 | try { 22 | Thread.sleep(1000); 23 | } catch (InterruptedException e) { 24 | e.printStackTrace(); 25 | } 26 | Map map = new HashMap<>(1); 27 | map.put("msg", "helloService (" + requestBody + "," + name + "," + id + "," + pwd + ") " + System.currentTimeMillis()); 28 | emitter.complete(map); 29 | }).start(); 30 | return emitter; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /netty-example-rpc-provider/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10001 3 | netty: 4 | enable-tcp-package-log: true 5 | max-connections: 10000 6 | compression: 7 | enabled: false 8 | min-response-size: 1B 9 | logging: 10 | level: 11 | com.github.netty: debug 12 | org: 13 | apache: 14 | tomcat: debug 15 | coyote: debug -------------------------------------------------------------------------------- /netty-example-rpc-provider/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: rpc-provider-service 4 | -------------------------------------------------------------------------------- /netty-example-servlet/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-servlet 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-servlet 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 2.5.12 25 | 26 | 27 | com.github.wangzihaogithub 28 | spring-boot-protocol 29 | 2.3.14 30 | 31 | 32 | mysql 33 | mysql-connector-java 34 | 8.0.28 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 3.8.1 45 | 46 | ${java.version} 47 | ${java.version} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /netty-example-servlet/src/main/java/com/github/netty/example/servlet/ServletApplication10002.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.servlet; 2 | 3 | import com.github.netty.springboot.EnableNettyEmbedded; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.web.bind.annotation.RequestBody; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import javax.servlet.http.HttpSession; 14 | import java.io.InputStream; 15 | import java.io.OutputStream; 16 | import java.security.Principal; 17 | import java.util.Map; 18 | 19 | /** 20 | * web服务器. 端口号:10002 (将tomcat换成了netty, 可以从request,response的实现看出来) 21 | *

22 | * Netty-servlet 适用于数据量小(例: 2M左右), 并发高的场景 23 | * 24 | * @author wangzihao (示例) 25 | */ 26 | @RestController 27 | @EnableNettyEmbedded//切换容器的注解 28 | @SpringBootApplication 29 | public class ServletApplication10002 { 30 | 31 | public static void main(String[] args) { 32 | SpringApplication.run(ServletApplication10002.class, args); 33 | } 34 | 35 | @RequestMapping("/hello") 36 | public Object hello(@RequestParam Map query, @RequestBody(required = false) Map body, 37 | HttpServletRequest request, HttpServletResponse response, 38 | HttpSession session, Principal principal, 39 | InputStream in, OutputStream out) { 40 | return "hello-servlet query=" + query + ",body=" + body; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /netty-example-servlet/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10002 3 | http2: 4 | enabled: true 5 | # netty: 6 | # http-servlet: 7 | # thread-pool: 8 | # enable: true 9 | # enable-h2c: true 10 | #logging: 11 | # level: 12 | # com.github.netty: debug -------------------------------------------------------------------------------- /netty-example-sse-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-sse-server 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-sse-server 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | com.github.wangzihaogithub 23 | sse-server 24 | 1.2.13 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 2.5.12 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 3.8.1 41 | 42 | ${java.version} 43 | ${java.version} 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /netty-example-sse-server/src/main/java/com/github/netty/example/server/SseApplication10005.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * Websocket服务端 端口号:10005 8 | * 访问 http://localhost:10005/index.html 可以看效果 9 | * 10 | * @author wangzihao 11 | */ 12 | @SpringBootApplication 13 | public class SseApplication10005 { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(SseApplication10005.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /netty-example-sse-server/src/main/java/com/github/netty/example/server/controller/MyAccessUser.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.server.controller; 2 | 3 | import com.github.sseserver.AccessToken; 4 | import com.github.sseserver.TenantAccessUser; 5 | 6 | /** 7 | * 当前登录用户 8 | * 9 | * @author wangzihao 10 | */ 11 | public class MyAccessUser implements com.github.sseserver.AccessUser, AccessToken, TenantAccessUser { 12 | private String accessToken; 13 | private String id; 14 | private Integer tenantId; 15 | 16 | @Override 17 | public String getAccessToken() { 18 | return accessToken; 19 | } 20 | 21 | public void setAccessToken(String accessToken) { 22 | this.accessToken = accessToken; 23 | } 24 | 25 | @Override 26 | public String getId() { 27 | return id; 28 | } 29 | 30 | public void setId(String id) { 31 | this.id = id; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return String.valueOf(id); 37 | } 38 | 39 | @Override 40 | public Integer getTenantId() { 41 | return tenantId; 42 | } 43 | 44 | public void setTenantId(Integer tenantId) { 45 | this.tenantId = tenantId; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /netty-example-sse-server/src/main/java/com/github/netty/example/server/controller/MySseWebController.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.server.controller; 2 | 3 | import com.github.sseserver.local.SseEmitter; 4 | import com.github.sseserver.local.SseWebController; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.Part; 13 | import java.io.IOException; 14 | import java.util.Collection; 15 | import java.util.Map; 16 | 17 | @RestController 18 | @RequestMapping("/my-sse") 19 | public class MySseWebController extends SseWebController { 20 | private static final Logger log = LoggerFactory.getLogger(MySseWebController.class); 21 | @Autowired 22 | private HttpServletRequest request; 23 | 24 | @Override 25 | protected Object onMessage(String path, SseEmitter connection, Map message) { 26 | if ("close".equals(path)) { 27 | connection.disconnect(); 28 | } else { 29 | log.info("onMessage path = {}", path); 30 | try { 31 | connection.send("server-ack", "onMessage " + path + message) 32 | .whenComplete((emitter, throwable) -> { 33 | log.info("onMessage path = {} whenComplete", path + message); 34 | }); 35 | } catch (IOException e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | return 1; 40 | } 41 | 42 | @Override 43 | protected Object onUpload(String path, SseEmitter connection, Map message, Collection files) { 44 | return super.onUpload(path, connection, message, files); 45 | } 46 | 47 | @Override 48 | protected MyAccessUser getAccessUser() { 49 | // 验证用户 50 | String token = request.getParameter("access-token"); 51 | MyAccessUser accessUser = new MyAccessUser(); 52 | accessUser.setId(token); 53 | accessUser.setAccessToken(token); 54 | accessUser.setTenantId(1); 55 | return accessUser; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /netty-example-sse-server/src/main/java/com/github/netty/example/server/controller/ServerSender.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.server.controller; 2 | 3 | import com.github.sseserver.local.LocalConnectionService; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.annotation.PostConstruct; 7 | import javax.annotation.Resource; 8 | import java.util.List; 9 | import java.util.concurrent.ScheduledThreadPoolExecutor; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | @Component 13 | public class ServerSender { 14 | @Resource 15 | private LocalConnectionService localConnectionService; 16 | 17 | @PostConstruct 18 | public void init() { 19 | new ScheduledThreadPoolExecutor(1) 20 | .scheduleWithFixedDelay(() -> { 21 | // 每5秒发送消息 22 | List users = localConnectionService.getUsers(); 23 | for (MyAccessUser user : users) { 24 | localConnectionService.sendByUserId(user.getId(), 25 | "server-push","{\"name\":\"ServerSender#sendByUserId 服务端推送的\"}" ); 26 | } 27 | }, 1, 3, TimeUnit.SECONDS); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /netty-example-sse-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10005 3 | -------------------------------------------------------------------------------- /netty-example-sse-server/src/main/resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | My WebSocket 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

17 | 18 | 19 | 87 | -------------------------------------------------------------------------------- /netty-example-websocket-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example-websocket-server 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | netty-example-websocket-server 12 | Demo project for Netty 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | com.github.wangzihaogithub 23 | spring-boot-protocol 24 | 2.3.14 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 2.5.12 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-websocket 37 | 2.1.2.RELEASE 38 | 39 | 40 | 41 | javax.websocket 42 | javax.websocket-api 43 | 1.1 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-compiler-plugin 52 | 3.8.1 53 | 54 | ${java.version} 55 | ${java.version} 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /netty-example-websocket-server/src/main/java/com/github/netty/example/server/WebsocketApplication10003.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.server; 2 | 3 | import com.github.netty.springboot.EnableNettyEmbedded; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.web.socket.config.annotation.EnableWebSocket; 7 | 8 | /** 9 | * Websocket服务端 端口号:10003 10 | * Netty-websocket 适用于数据量小(例: 2M左右), 并发高的场景 11 | *访问 http://localhost:10003/index.html 可以看效果 12 | * @author wangzihao 13 | */ 14 | @EnableWebSocket 15 | @EnableNettyEmbedded//这里需要切换为netty容器 16 | @SpringBootApplication 17 | public class WebsocketApplication10003 { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(WebsocketApplication10003.class, args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /netty-example-websocket-server/src/main/java/com/github/netty/example/server/controller/WebsocketController.java: -------------------------------------------------------------------------------- 1 | package com.github.netty.example.server.controller; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.http.server.ServerHttpRequest; 6 | import org.springframework.http.server.ServerHttpResponse; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.socket.*; 9 | import org.springframework.web.socket.config.annotation.WebSocketConfigurer; 10 | import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; 11 | import org.springframework.web.socket.handler.AbstractWebSocketHandler; 12 | import org.springframework.web.socket.server.HandshakeInterceptor; 13 | 14 | import java.io.IOException; 15 | import java.util.Map; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | import java.util.concurrent.ScheduledThreadPoolExecutor; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | /** 21 | * websocket接口控制器 22 | * 23 | * @author wangzihao 2022年3月19日19:48:58 24 | */ 25 | @Component 26 | public class WebsocketController extends AbstractWebSocketHandler implements WebSocketConfigurer, HandshakeInterceptor { 27 | public static final Map sessionMap = new ConcurrentHashMap<>(); 28 | private static final Logger log = LoggerFactory.getLogger(WebsocketController.class); 29 | 30 | static { 31 | new ScheduledThreadPoolExecutor(1) 32 | .scheduleWithFixedDelay(() -> { 33 | // 每秒发送消息 34 | for (WebSocketSession session : WebsocketController.sessionMap.values()) { 35 | try { 36 | session.sendMessage(new TextMessage("服务端推送的")); 37 | } catch (IOException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | }, 1, 1, TimeUnit.SECONDS); 42 | } 43 | 44 | @Override 45 | public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 46 | log.info("注册websocket {}", getClass()); 47 | registry.addHandler(this, "/my-websocket") 48 | .addInterceptors(this).setAllowedOrigins("*"); 49 | } 50 | 51 | @Override 52 | public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { 53 | log.info("握手前登录身份验证"); 54 | return true; 55 | } 56 | 57 | @Override 58 | public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { 59 | log.info("握手后记录日志"); 60 | } 61 | 62 | @Override 63 | public void afterConnectionEstablished(WebSocketSession session) throws Exception { 64 | log.info("建立链接保存会话"); 65 | sessionMap.put(session.getId(), session); 66 | } 67 | 68 | @Override 69 | public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { 70 | log.info("WebSocket服务端关闭: 关闭连接状态: " + status); 71 | } 72 | 73 | @Override 74 | protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { 75 | log.info("接受来自客户端发送的文本信息: " + message.getPayload().toString()); 76 | } 77 | 78 | @Override 79 | protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception { 80 | log.info("接受来自客户端发送的二进制信息: " + message.getPayload().toString()); 81 | } 82 | 83 | @Override 84 | public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { 85 | log.info("WebSocket服务端异常:连接异常信息: " + exception.getMessage()); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /netty-example-websocket-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10003 3 | netty: 4 | enable-tcp-package-log: false 5 | logging: 6 | level: 7 | com.github.netty: info 8 | -------------------------------------------------------------------------------- /netty-example-websocket-server/src/main/resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My WebSocket 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 65 | -------------------------------------------------------------------------------- /netty-example-websocket-server/src/test/java/WebsocketTest.java: -------------------------------------------------------------------------------- 1 | //import org.slf4j.Logger; 2 | //import org.slf4j.LoggerFactory; 3 | //import org.springframework.messaging.converter.MappingJackson2MessageConverter; 4 | //import org.springframework.messaging.simp.stomp.StompHeaders; 5 | //import org.springframework.messaging.simp.stomp.StompSession; 6 | //import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; 7 | //import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 8 | //import org.springframework.web.socket.WebSocketHttpHeaders; 9 | //import org.springframework.web.socket.client.standard.StandardWebSocketClient; 10 | //import org.springframework.web.socket.messaging.WebSocketStompClient; 11 | //import org.springframework.web.socket.sockjs.client.SockJsClient; 12 | //import org.springframework.web.socket.sockjs.client.WebSocketTransport; 13 | // 14 | //import java.util.*; 15 | //import java.util.concurrent.CopyOnWriteArrayList; 16 | //import java.util.concurrent.Executors; 17 | //import java.util.concurrent.ScheduledExecutorService; 18 | //import java.util.concurrent.TimeUnit; 19 | //import java.util.concurrent.atomic.AtomicInteger; 20 | // 21 | ///** 22 | // * 客户端测试 ( Websocket服务端口 : 10003) 23 | // * 从Http协议协商至Websocket STOMP协议 24 | // * @author wangzihao 25 | // */ 26 | //public class WebsocketTest { 27 | // 28 | // public static void main(String[] args) { 29 | // ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(3); 30 | // //发起连接的次数 31 | // AtomicInteger connectCount = new AtomicInteger(); 32 | // //连接成功数 33 | // AtomicInteger successCount = new AtomicInteger(); 34 | // //连接失败数 35 | // AtomicInteger errorCount = new AtomicInteger(); 36 | // //链接的列表 37 | // List sessionList = new CopyOnWriteArrayList<>(); 38 | // //订阅的列表 39 | // List subscriptionList = new CopyOnWriteArrayList<>(); 40 | // 41 | // //连接并订阅 42 | // String url = "ws://localhost:10003/my-websocket?access-token=b90b0e77-63cf-4b05-8d8b-43ebefc71a6a"; 43 | // Runnable connectRunnable = newConnectAndSubscribeRunnable(url, connectCount, successCount, errorCount, sessionList, subscriptionList); 44 | // scheduledService.scheduleAtFixedRate(connectRunnable, 0, 1000, TimeUnit.MILLISECONDS);//1秒间隔 一次新连接 45 | // 46 | // //发送消息 47 | // Runnable sendMessageRunnable = newSendMessageRunnable(sessionList); 48 | // scheduledService.scheduleAtFixedRate(sendMessageRunnable, 0, 1000, TimeUnit.MILLISECONDS);//1秒间隔 所有会话发送消息 49 | // 50 | // scheduledService.scheduleAtFixedRate(() -> { 51 | // //每次5 秒打印一次详情 52 | // logger.info(" 连接数:" + connectCount + " 成功数:" + successCount + " 失败数:" + errorCount); 53 | // }, 5, 5, TimeUnit.SECONDS); 54 | // } 55 | // 56 | // /** 57 | // * 发送消息 58 | // * 59 | // * @param sessionList 60 | // * @return 61 | // */ 62 | // private static Runnable newSendMessageRunnable(List sessionList) { 63 | // return new Runnable() { 64 | // @Override 65 | // public void run() { 66 | // int i = 0; 67 | // for (StompSession session : sessionList) { 68 | // i++; 69 | // 70 | // StompHeaders headers = new StompHeaders(); 71 | // headers.setDestination("/app/receiveMessage"); 72 | // headers.set("my-login-user", "小" + i); 73 | // 74 | // Map payload = new HashMap<>(2); 75 | // payload.put("msg", "你好"); 76 | // 77 | // session.send(headers, payload); 78 | // } 79 | // } 80 | // }; 81 | // } 82 | // 83 | // private static Logger logger = LoggerFactory.getLogger(WebsocketTest.class); 84 | // private static ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); 85 | // private static MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter(); 86 | // private static WebSocketStompClient client = new WebSocketStompClient(new SockJsClient(Arrays.asList(new WebSocketTransport(new StandardWebSocketClient())))); 87 | // 88 | // static { 89 | // taskScheduler.afterPropertiesSet(); 90 | // client.setMessageConverter(messageConverter); 91 | // client.setTaskScheduler(taskScheduler); 92 | // } 93 | // 94 | // /** 95 | // * 连接 并且 订阅一个主题 96 | // * 97 | // * @param url 98 | // * @param connectCount 99 | // * @param successCount 100 | // * @param errorCount 101 | // * @param connectList 102 | // * @return 103 | // */ 104 | // private static Runnable newConnectAndSubscribeRunnable(String url, AtomicInteger connectCount, AtomicInteger successCount, AtomicInteger errorCount, 105 | // List connectList, List subscriptionList) { 106 | // return new Runnable() { 107 | // int i = 0; 108 | // Random random = new Random(); 109 | // 110 | // @Override 111 | // public void run() { 112 | // try { 113 | // WebSocketHttpHeaders httpHeaders = new WebSocketHttpHeaders(); 114 | // String accessToken = "user" + i; 115 | // httpHeaders.add("access-token", accessToken); 116 | // //连接至url 117 | // StompSession session = client.connect(url, httpHeaders, new StompSessionHandlerAdapter() { 118 | // @Override 119 | // public void afterConnected(StompSession session, StompHeaders headers) { 120 | // successCount.incrementAndGet(); 121 | // } 122 | // 123 | // @Override 124 | // public void handleTransportError(StompSession session, Throwable exception) { 125 | // errorCount.incrementAndGet(); 126 | // } 127 | // }).get(); 128 | // connectList.add(session); 129 | // 130 | // //订阅一个主题 131 | // String destination = "/app/user/room/user" + random.nextInt(connectList.size()) + "/room" + random.nextInt(connectList.size()); 132 | // StompSession.Subscription subscription = session.subscribe(destination, new StompSessionHandlerAdapter() { 133 | // }); 134 | // subscriptionList.add(subscription); 135 | // 136 | // i++; 137 | // } catch (Exception e) { 138 | // // 139 | // } finally { 140 | // connectCount.incrementAndGet(); 141 | // } 142 | // } 143 | // }; 144 | // } 145 | // 146 | //} 147 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | netty-example 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | netty-example 12 | 13 | 14 | netty-example-qps 15 | netty-example-rpc-api 16 | netty-example-getway 17 | netty-example-rpc-consumer 18 | netty-example-rpc-provider 19 | netty-example-servlet 20 | netty-example-sse-server 21 | netty-example-websocket-server 22 | spring-boot-example-protocol 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.0 31 | 32 | 1.8 33 | 1.8 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /spring-boot-example-protocol/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.wangzihaogithub 7 | spring-boot-example-protocol 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring-boot-example-protocol 12 | Demo project for Spring Boot Protocol 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 2.5.12 26 | 27 | 28 | 29 | com.github.wangzihaogithub 30 | spring-boot-protocol 31 | 2.3.14 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 3.8.1 41 | 42 | ${java.version} 43 | ${java.version} 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /spring-boot-example-protocol/src/main/java/com/github/example/ProtocolApplication10004.java: -------------------------------------------------------------------------------- 1 | package com.github.example; 2 | 3 | import com.github.example.protocol.MyProtocol; 4 | import com.github.netty.protocol.MqttProtocol; 5 | import com.github.netty.springboot.EnableNettyEmbedded; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | /** 11 | * Spring动态协议扩展 端口号:10004 12 | * 适用于多协议开发的场景 13 | * 14 | * @author wangzihao 15 | */ 16 | @EnableNettyEmbedded//这里需要切换为netty容器 17 | @SpringBootApplication 18 | public class ProtocolApplication10004 { 19 | 20 | /** 21 | * 添加私有协议 22 | * 23 | * @return 24 | */ 25 | @Bean 26 | public MyProtocol myProtocol() { 27 | return new MyProtocol(); 28 | } 29 | 30 | /** 31 | * 添加mqtt协议 32 | * 33 | * @return 34 | */ 35 | @Bean 36 | public MqttProtocol mqttProtocolsRegister() { 37 | return new MqttProtocol(); 38 | } 39 | 40 | public static void main(String[] args) { 41 | SpringApplication.run(ProtocolApplication10004.class, args); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /spring-boot-example-protocol/src/main/java/com/github/example/protocol/MyChannelHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.example.protocol; 2 | 3 | import com.github.netty.core.AbstractChannelHandler; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * 2019/2/28/028. 8 | * 消息处理器 9 | * 10 | * @author acer01 11 | */ 12 | public class MyChannelHandler extends AbstractChannelHandler { 13 | 14 | @Override 15 | protected void onMessageReceived(ChannelHandlerContext ctx, String msg) throws Exception { 16 | ctx.writeAndFlush("\r\n hello " + msg + "\r\n"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-example-protocol/src/main/java/com/github/example/protocol/MyProtocol.java: -------------------------------------------------------------------------------- 1 | package com.github.example.protocol; 2 | 3 | import com.github.netty.core.AbstractNettyServer; 4 | import com.github.netty.core.AbstractProtocol; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.Channel; 7 | import io.netty.handler.codec.LineBasedFrameDecoder; 8 | import io.netty.handler.codec.string.StringDecoder; 9 | import io.netty.handler.codec.string.StringEncoder; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * 2019/2/28/028. 15 | * 私有协议注册 16 | * 17 | * @author wangzihao 18 | */ 19 | public class MyProtocol extends AbstractProtocol { 20 | //协议头 21 | public static final byte[] PROTOCOL_HEADER = { 22 | 'M', 'Y', 23 | 'H', 'E', 'A', 'D', 'E', 'R' 24 | }; 25 | private Logger logger = LoggerFactory.getLogger(getClass()); 26 | 27 | @Override 28 | public String getProtocolName() { 29 | return "my-protocol"; 30 | } 31 | 32 | /** 33 | * 第一个消息决定,该连接以后传输的协议 34 | * 35 | * @param clientFirstMsg 消息 36 | * @return 是否支持此消息类型 37 | */ 38 | @Override 39 | public boolean canSupport(ByteBuf clientFirstMsg) { 40 | if (clientFirstMsg.readableBytes() < PROTOCOL_HEADER.length) { 41 | return false; 42 | } 43 | for (int i = 0; i < PROTOCOL_HEADER.length; i++) { 44 | if (clientFirstMsg.getByte(i) != PROTOCOL_HEADER[i]) { 45 | return false; 46 | } 47 | } 48 | return true; 49 | } 50 | 51 | @Override 52 | public void addPipeline(Channel channel, ByteBuf clientFirstMsg) throws Exception { 53 | channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); 54 | channel.pipeline().addLast(new StringDecoder()); 55 | channel.pipeline().addLast(new StringEncoder()); 56 | channel.pipeline().addLast(new MyChannelHandler()); 57 | } 58 | 59 | @Override 60 | public int getOrder() { 61 | return 0; 62 | } 63 | 64 | @Override 65 | public void onServerStart(T server) throws Exception { 66 | logger.info("私有协议启动!"); 67 | } 68 | 69 | @Override 70 | public void onServerStop(T server) throws Exception { 71 | logger.info("私有协议停止!"); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /spring-boot-example-protocol/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 10004 3 | netty: 4 | enable-tcp-package-log: true --------------------------------------------------------------------------------