之前寫過springboot和websocket整合的例子,https://blog.csdn.net/u014203449/article/details/102902078
在微服務中,頁面與後臺服務器的交互一般要通過網關,所以網關是否支持長鏈接也得調試一把。
springcloud中有兩個網關組件zuul和gateway。
經查閱zuul是不支持長鏈接的,而gateway支持長鏈接。並且zuul底層是同步阻塞基於servlet,而gateway是基於netty、webflux異步非堵塞,性能更好。
現在來看看gateway和websoket的整合。
建立一個eureka項目,不贅述了。
建立提供websocket業務的服務wisdomclass-demo,註冊到註冊中心,websoket的配置也是和上篇文章一樣。https://blog.csdn.net/u014203449/article/details/102902078
主要是gateway的配置,上個配置文件。主要看cloud.gateway.routes的配置:
server:
port: 9006
spring:
application:
name: wisdomclass-gateway
servlet:
multipart:
enabled: true #使用http multipart上傳處理
max-file-size: 100MB #單個文件的最大長度爲100MB,默認1mb
max-request-size: 100MB #請求的總數據大小
file-size-threshold: 1MB #文件大小閾值,當大於這個閾值時將寫入到磁盤,否則存在內存中,(默認值0 一般情況下不用特意修改)
location: / #超過以上閾值時,寫入的臨時目錄,當請求響應完成後,臨時文件自動刪除
cloud:
gateway:
routes:
- id: demo
order: 5
uri: lb://wisdomclass-demo #lb代表從註冊中心獲取服務,將path的請求路由到uri
predicates:
- Path=/wisdomclass-demo/**
filters:
- StripPrefix=1 #除去第一個/前綴,比如請求/wisdomclass-demo/demo,會去除前綴/wisdomclass-demo,請求到路由服務的 /demo接口
- name: Hystrix #熔斷
args:
name: fallbackcmd
fallbackUri: forward:/fallback #網關的統一熔斷接口
- id: demo
uri: lb:ws://wisdomclass-demo #wesocket協議
order: 2
predicates:
- Path=/wisdomclass-demo/topic/**
filters:
- StripPrefix=1
- id: demo
uri: lb:ws://wisdomclass-demo
order: 3
predicates:
- Path=/wisdomclass-demo/app/**
filters:
- StripPrefix=1
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 #超時時間後進入熔斷
#eureka客戶端配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9000/eureka/
logging:
level:
root: info
主要看cloud.gateway.routes的配置:
id只是個路由標誌,我認爲沒有什麼用,兩個路由配置id可以相同。
order是執行順序,兩個id相同的路由配置會執行order數值小的。
predicates:是判斷請求地址是否符合 value,如果符合會路由到uri 的value地址。
uri:請求符合predicates的,會路由到uri地址。
這個地方其實可以直接寫路由的地址,比如baidu.com,會直接將請求轉發到baidu。但微服務實例通常有很多,這裏不能直接寫服務ip端口,如果想按註冊中心的服務名來路由,前面要加 lb://, lb://wisdomclass-demo這個配置是路由到服務名爲wisdomclass-demo的服務,並且用http協議的方式。
但是wisdomclass-demo這個項目裏有關於websocket的內容,用的是websocket協議,不是http,所以在routes配置中,寫了多個路由配置,我寫的配置是 以app 和topic開頭的url,用ws協議路由到wisdomclass-demo服務去通信。其餘的請求還是http。因爲第二個第三個路由配置的order更小,所以優先執行這兩個路由規則,最後執行第一個路由規則。
爲什麼是app和topic?因爲我在stomp設置的頻道是他們:
weosocket配置:
package cn.sosuncloud.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import javax.servlet.http.HttpSession;
import java.util.Map;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic","/q");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
// @Override
// public void registerStompEndpoints(StompEndpointRegistry registry) {
// registry.addEndpoint("/gs-guide-websocket").withSockJS();
// }
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/gs-guide-websocket")
.setHandshakeHandler(new DefaultHandshakeHandler() {
public boolean beforeHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest
= (ServletServerHttpRequest) request;
HttpSession session = servletRequest
.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
HttpHeaders headers = servletRequest.getHeaders();
}
return true;
}}).withSockJS();
}
/**
* 配置客戶端入站通道攔截器
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(createUserInterceptor());
}
/**
*
* @Title: createUserInterceptor
* @Description: 將客戶端渠道攔截器加入spring ioc容器
* @return
*/
@Bean
public UserInterceptor createUserInterceptor() {
return new UserInterceptor();
}
}
頻道接受消息和推送:
@Controller
public class GreetingController {
@Autowired
SimpMessagingTemplate SMT;
@MessageMapping(value = "/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
可以看到,只有服務端向客戶端推送消息的協議是ws,也就是app 和topick的url。
其餘建立連接的url 如 /gs-guide-websocket ,和客戶端發送消息的url /hello 還是http。
這裏stomp建立連接用的是http。