yym68686 commited on
Commit
ac2ca17
·
1 Parent(s): 58f793c

🐛 Bug: 1. Fix the bug where different base URLs use the same client.

Browse files

2. Fix the bug where Vercel uses a different event loop, causing httpx's connection pool to attempt to reuse the same connection in a different event loop, which is not allowed in asyncio.

💰 Sponsors: Thanks to @PowerHunter for the ¥1800 sponsorship, sponsorship information has been added to the README.

Files changed (3) hide show
  1. README.md +1 -1
  2. README_CN.md +1 -1
  3. main.py +23 -16
README.md CHANGED
@@ -333,7 +333,7 @@ curl -X POST http://127.0.0.1:8000/v1/chat/completions \
333
 
334
  We thank the following sponsors for their support:
335
  <!-- ¥1000 -->
336
- - @PowerHunter: ¥1400
337
 
338
  ## How to sponsor us
339
 
 
333
 
334
  We thank the following sponsors for their support:
335
  <!-- ¥1000 -->
336
+ - @PowerHunter: ¥1800
337
 
338
  ## How to sponsor us
339
 
README_CN.md CHANGED
@@ -333,7 +333,7 @@ curl -X POST http://127.0.0.1:8000/v1/chat/completions \
333
 
334
  我们感谢以下赞助商的支持:
335
  <!-- ¥1000 -->
336
- - @PowerHunter:¥1400
337
 
338
  ## 如何赞助我们
339
 
 
333
 
334
  我们感谢以下赞助商的支持:
335
  <!-- ¥1000 -->
336
+ - @PowerHunter:¥1800
337
 
338
  ## 如何赞助我们
339
 
main.py CHANGED
@@ -48,6 +48,8 @@ from sqlalchemy.sql import sqltypes
48
 
49
  # 添加新的环境变量检查
50
  DISABLE_DATABASE = os.getenv("DISABLE_DATABASE", "false").lower() == "true"
 
 
51
 
52
  async def create_tables():
53
  if DISABLE_DATABASE:
@@ -594,16 +596,28 @@ app.add_middleware(StatsMiddleware)
594
  class ClientManager:
595
  def __init__(self, pool_size=100):
596
  self.pool_size = pool_size
597
- self.clients = {} # {timeout_value: AsyncClient}
598
 
599
  async def init(self, default_config):
600
  self.default_config = default_config
601
 
602
  @asynccontextmanager
603
- async def get_client(self, timeout_value, proxy=None):
604
  # 直接获取或创建客户端,不使用锁
605
  timeout_value = int(timeout_value)
606
- if timeout_value not in self.clients:
 
 
 
 
 
 
 
 
 
 
 
 
607
  timeout = httpx.Timeout(
608
  connect=15.0,
609
  read=timeout_value,
@@ -620,39 +634,32 @@ class ClientManager:
620
 
621
  if proxy:
622
  # 解析代理URL
623
- from urllib.parse import urlparse
624
  parsed = urlparse(proxy)
625
-
626
- # 修改这里: 将 socks5h 转换为 socks5
627
  scheme = parsed.scheme.rstrip('h')
628
- # print("scheme", scheme)
629
 
630
  if scheme == 'socks5':
631
  try:
632
  from httpx_socks import AsyncProxyTransport
633
- # 使用修改后的scheme创建代理URL
634
  proxy = proxy.replace('socks5h://', 'socks5://')
635
- # 创建SOCKS5代理传输
636
  transport = AsyncProxyTransport.from_url(proxy)
637
  client_config["transport"] = transport
638
  except ImportError:
639
  logger.error("httpx-socks package is required for SOCKS proxy support")
640
  raise ImportError("Please install httpx-socks package for SOCKS proxy support: pip install httpx-socks")
641
  else:
642
- # 对于HTTP/HTTPS代理使用原有方式
643
  client_config["proxies"] = {
644
  "http://": proxy,
645
  "https://": proxy
646
  }
647
 
648
- self.clients[timeout_value] = httpx.AsyncClient(**client_config)
649
 
650
  try:
651
- yield self.clients[timeout_value]
652
  except Exception as e:
653
- if timeout_value in self.clients:
654
- tmp_client = self.clients[timeout_value]
655
- del self.clients[timeout_value] # 先删除引用
656
  await tmp_client.aclose() # 然后关闭客户端
657
  raise e
658
 
@@ -830,7 +837,7 @@ async def process_request(request: Union[RequestModel, ImageGenerationRequest, A
830
  # print("proxy", proxy)
831
 
832
  try:
833
- async with app.state.client_manager.get_client(timeout_value, proxy) as client:
834
  # 打印client配置信息
835
  # logger.info(f"Client config - Timeout: {client.timeout}")
836
  # logger.info(f"Client config - Headers: {client.headers}")
 
48
 
49
  # 添加新的环境变量检查
50
  DISABLE_DATABASE = os.getenv("DISABLE_DATABASE", "false").lower() == "true"
51
+ IS_VERCEL = os.path.dirname(os.path.abspath(__file__)).startswith('/var/task')
52
+ logger.info("IS_VERCEL: %s", IS_VERCEL)
53
 
54
  async def create_tables():
55
  if DISABLE_DATABASE:
 
596
  class ClientManager:
597
  def __init__(self, pool_size=100):
598
  self.pool_size = pool_size
599
+ self.clients = {} # {host_timeout_proxy: AsyncClient}
600
 
601
  async def init(self, default_config):
602
  self.default_config = default_config
603
 
604
  @asynccontextmanager
605
+ async def get_client(self, timeout_value, base_url, proxy=None):
606
  # 直接获取或创建客户端,不使用锁
607
  timeout_value = int(timeout_value)
608
+
609
+ # 从base_url中提取主机名
610
+ parsed_url = urlparse(base_url)
611
+ host = parsed_url.netloc
612
+
613
+ # 创建唯一的客户端键
614
+ client_key = f"{host}_{timeout_value}"
615
+ if proxy:
616
+ # 对代理URL进行规范化处理
617
+ proxy_normalized = proxy.replace('socks5h://', 'socks5://')
618
+ client_key += f"_{proxy_normalized}"
619
+
620
+ if client_key not in self.clients or IS_VERCEL:
621
  timeout = httpx.Timeout(
622
  connect=15.0,
623
  read=timeout_value,
 
634
 
635
  if proxy:
636
  # 解析代理URL
 
637
  parsed = urlparse(proxy)
 
 
638
  scheme = parsed.scheme.rstrip('h')
 
639
 
640
  if scheme == 'socks5':
641
  try:
642
  from httpx_socks import AsyncProxyTransport
 
643
  proxy = proxy.replace('socks5h://', 'socks5://')
 
644
  transport = AsyncProxyTransport.from_url(proxy)
645
  client_config["transport"] = transport
646
  except ImportError:
647
  logger.error("httpx-socks package is required for SOCKS proxy support")
648
  raise ImportError("Please install httpx-socks package for SOCKS proxy support: pip install httpx-socks")
649
  else:
 
650
  client_config["proxies"] = {
651
  "http://": proxy,
652
  "https://": proxy
653
  }
654
 
655
+ self.clients[client_key] = httpx.AsyncClient(**client_config)
656
 
657
  try:
658
+ yield self.clients[client_key]
659
  except Exception as e:
660
+ if client_key in self.clients:
661
+ tmp_client = self.clients[client_key]
662
+ del self.clients[client_key] # 先删除引用
663
  await tmp_client.aclose() # 然后关闭客户端
664
  raise e
665
 
 
837
  # print("proxy", proxy)
838
 
839
  try:
840
+ async with app.state.client_manager.get_client(timeout_value, url, proxy) as client:
841
  # 打印client配置信息
842
  # logger.info(f"Client config - Timeout: {client.timeout}")
843
  # logger.info(f"Client config - Headers: {client.headers}")